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

playback.c (11436B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Line 6 Linux USB driver
      4 *
      5 * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
      6 */
      7
      8#include <linux/slab.h>
      9#include <sound/core.h>
     10#include <sound/pcm.h>
     11#include <sound/pcm_params.h>
     12
     13#include "capture.h"
     14#include "driver.h"
     15#include "pcm.h"
     16#include "playback.h"
     17
     18/*
     19	Software stereo volume control.
     20*/
     21static void change_volume(struct urb *urb_out, int volume[],
     22			  int bytes_per_frame)
     23{
     24	int chn = 0;
     25
     26	if (volume[0] == 256 && volume[1] == 256)
     27		return;		/* maximum volume - no change */
     28
     29	if (bytes_per_frame == 4) {
     30		__le16 *p, *buf_end;
     31
     32		p = (__le16 *)urb_out->transfer_buffer;
     33		buf_end = p + urb_out->transfer_buffer_length / sizeof(*p);
     34
     35		for (; p < buf_end; ++p) {
     36			short pv = le16_to_cpu(*p);
     37			int val = (pv * volume[chn & 1]) >> 8;
     38			pv = clamp(val, -0x8000, 0x7fff);
     39			*p = cpu_to_le16(pv);
     40			++chn;
     41		}
     42	} else if (bytes_per_frame == 6) {
     43		unsigned char *p, *buf_end;
     44
     45		p = (unsigned char *)urb_out->transfer_buffer;
     46		buf_end = p + urb_out->transfer_buffer_length;
     47
     48		for (; p < buf_end; p += 3) {
     49			int val;
     50
     51			val = p[0] + (p[1] << 8) + ((signed char)p[2] << 16);
     52			val = (val * volume[chn & 1]) >> 8;
     53			val = clamp(val, -0x800000, 0x7fffff);
     54			p[0] = val;
     55			p[1] = val >> 8;
     56			p[2] = val >> 16;
     57			++chn;
     58		}
     59	}
     60}
     61
     62/*
     63	Create signal for impulse response test.
     64*/
     65static void create_impulse_test_signal(struct snd_line6_pcm *line6pcm,
     66				       struct urb *urb_out, int bytes_per_frame)
     67{
     68	int frames = urb_out->transfer_buffer_length / bytes_per_frame;
     69
     70	if (bytes_per_frame == 4) {
     71		int i;
     72		short *pi = (short *)line6pcm->prev_fbuf;
     73		short *po = (short *)urb_out->transfer_buffer;
     74
     75		for (i = 0; i < frames; ++i) {
     76			po[0] = pi[0];
     77			po[1] = 0;
     78			pi += 2;
     79			po += 2;
     80		}
     81	} else if (bytes_per_frame == 6) {
     82		int i, j;
     83		unsigned char *pi = line6pcm->prev_fbuf;
     84		unsigned char *po = urb_out->transfer_buffer;
     85
     86		for (i = 0; i < frames; ++i) {
     87			for (j = 0; j < bytes_per_frame / 2; ++j)
     88				po[j] = pi[j];
     89
     90			for (; j < bytes_per_frame; ++j)
     91				po[j] = 0;
     92
     93			pi += bytes_per_frame;
     94			po += bytes_per_frame;
     95		}
     96	}
     97	if (--line6pcm->impulse_count <= 0) {
     98		((unsigned char *)(urb_out->transfer_buffer))[bytes_per_frame -
     99							      1] =
    100		    line6pcm->impulse_volume;
    101		line6pcm->impulse_count = line6pcm->impulse_period;
    102	}
    103}
    104
    105/*
    106	Add signal to buffer for software monitoring.
    107*/
    108static void add_monitor_signal(struct urb *urb_out, unsigned char *signal,
    109			       int volume, int bytes_per_frame)
    110{
    111	if (volume == 0)
    112		return;		/* zero volume - no change */
    113
    114	if (bytes_per_frame == 4) {
    115		__le16 *pi, *po, *buf_end;
    116
    117		pi = (__le16 *)signal;
    118		po = (__le16 *)urb_out->transfer_buffer;
    119		buf_end = po + urb_out->transfer_buffer_length / sizeof(*po);
    120
    121		for (; po < buf_end; ++pi, ++po) {
    122			short pov = le16_to_cpu(*po);
    123			short piv = le16_to_cpu(*pi);
    124			int val = pov + ((piv * volume) >> 8);
    125			pov = clamp(val, -0x8000, 0x7fff);
    126			*po = cpu_to_le16(pov);
    127		}
    128	}
    129
    130	/*
    131	   We don't need to handle devices with 6 bytes per frame here
    132	   since they all support hardware monitoring.
    133	 */
    134}
    135
    136/*
    137	Find a free URB, prepare audio data, and submit URB.
    138	must be called in line6pcm->out.lock context
    139*/
    140static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
    141{
    142	int index;
    143	int i, urb_size, urb_frames;
    144	int ret;
    145	const int bytes_per_frame =
    146		line6pcm->properties->bytes_per_channel *
    147		line6pcm->properties->playback_hw.channels_max;
    148	const int frame_increment =
    149		line6pcm->properties->rates.rats[0].num_min;
    150	const int frame_factor =
    151		line6pcm->properties->rates.rats[0].den *
    152		(line6pcm->line6->intervals_per_second / LINE6_ISO_INTERVAL);
    153	struct urb *urb_out;
    154
    155	index = find_first_zero_bit(&line6pcm->out.active_urbs,
    156				    line6pcm->line6->iso_buffers);
    157
    158	if (index < 0 || index >= line6pcm->line6->iso_buffers) {
    159		dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
    160		return -EINVAL;
    161	}
    162
    163	urb_out = line6pcm->out.urbs[index];
    164	urb_size = 0;
    165
    166	/* TODO: this may not work for LINE6_ISO_PACKETS != 1 */
    167	for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
    168		/* compute frame size for given sampling rate */
    169		int fsize = 0;
    170		struct usb_iso_packet_descriptor *fout =
    171		    &urb_out->iso_frame_desc[i];
    172
    173		fsize = line6pcm->prev_fsize;
    174		if (fsize == 0) {
    175			int n;
    176
    177			line6pcm->out.count += frame_increment;
    178			n = line6pcm->out.count / frame_factor;
    179			line6pcm->out.count -= n * frame_factor;
    180			fsize = n;
    181		}
    182
    183		fsize *= bytes_per_frame;
    184
    185		fout->offset = urb_size;
    186		fout->length = fsize;
    187		urb_size += fsize;
    188	}
    189
    190	if (urb_size == 0) {
    191		/* can't determine URB size */
    192		dev_err(line6pcm->line6->ifcdev, "driver bug: urb_size = 0\n");
    193		return -EINVAL;
    194	}
    195
    196	urb_frames = urb_size / bytes_per_frame;
    197	urb_out->transfer_buffer =
    198	    line6pcm->out.buffer +
    199	    index * LINE6_ISO_PACKETS * line6pcm->max_packet_size_out;
    200	urb_out->transfer_buffer_length = urb_size;
    201	urb_out->context = line6pcm;
    202
    203	if (test_bit(LINE6_STREAM_PCM, &line6pcm->out.running) &&
    204	    !test_bit(LINE6_FLAG_PAUSE_PLAYBACK, &line6pcm->flags)) {
    205		struct snd_pcm_runtime *runtime =
    206		    get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK)->runtime;
    207
    208		if (line6pcm->out.pos + urb_frames > runtime->buffer_size) {
    209			/*
    210			   The transferred area goes over buffer boundary,
    211			   copy the data to the temp buffer.
    212			 */
    213			int len;
    214
    215			len = runtime->buffer_size - line6pcm->out.pos;
    216
    217			if (len > 0) {
    218				memcpy(urb_out->transfer_buffer,
    219				       runtime->dma_area +
    220				       line6pcm->out.pos * bytes_per_frame,
    221				       len * bytes_per_frame);
    222				memcpy(urb_out->transfer_buffer +
    223				       len * bytes_per_frame, runtime->dma_area,
    224				       (urb_frames - len) * bytes_per_frame);
    225			} else
    226				dev_err(line6pcm->line6->ifcdev, "driver bug: len = %d\n",
    227					len);
    228		} else {
    229			memcpy(urb_out->transfer_buffer,
    230			       runtime->dma_area +
    231			       line6pcm->out.pos * bytes_per_frame,
    232			       urb_out->transfer_buffer_length);
    233		}
    234
    235		line6pcm->out.pos += urb_frames;
    236		if (line6pcm->out.pos >= runtime->buffer_size)
    237			line6pcm->out.pos -= runtime->buffer_size;
    238
    239		change_volume(urb_out, line6pcm->volume_playback,
    240			      bytes_per_frame);
    241	} else {
    242		memset(urb_out->transfer_buffer, 0,
    243		       urb_out->transfer_buffer_length);
    244	}
    245
    246	spin_lock_nested(&line6pcm->in.lock, SINGLE_DEPTH_NESTING);
    247	if (line6pcm->prev_fbuf) {
    248		if (test_bit(LINE6_STREAM_IMPULSE, &line6pcm->out.running)) {
    249			create_impulse_test_signal(line6pcm, urb_out,
    250						   bytes_per_frame);
    251			if (test_bit(LINE6_STREAM_PCM, &line6pcm->in.running)) {
    252				line6_capture_copy(line6pcm,
    253						   urb_out->transfer_buffer,
    254						   urb_out->
    255						   transfer_buffer_length);
    256				line6_capture_check_period(line6pcm,
    257					urb_out->transfer_buffer_length);
    258			}
    259		} else {
    260			if (!(line6pcm->line6->properties->capabilities & LINE6_CAP_HWMON)
    261			    && line6pcm->out.running && line6pcm->in.running)
    262				add_monitor_signal(urb_out, line6pcm->prev_fbuf,
    263						   line6pcm->volume_monitor,
    264						   bytes_per_frame);
    265		}
    266		line6pcm->prev_fbuf = NULL;
    267		line6pcm->prev_fsize = 0;
    268	}
    269	spin_unlock(&line6pcm->in.lock);
    270
    271	ret = usb_submit_urb(urb_out, GFP_ATOMIC);
    272
    273	if (ret == 0)
    274		set_bit(index, &line6pcm->out.active_urbs);
    275	else
    276		dev_err(line6pcm->line6->ifcdev,
    277			"URB out #%d submission failed (%d)\n", index, ret);
    278
    279	return 0;
    280}
    281
    282/*
    283	Submit all currently available playback URBs.
    284	must be called in line6pcm->out.lock context
    285 */
    286int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm)
    287{
    288	int ret = 0, i;
    289
    290	for (i = 0; i < line6pcm->line6->iso_buffers; ++i) {
    291		ret = submit_audio_out_urb(line6pcm);
    292		if (ret < 0)
    293			break;
    294	}
    295
    296	return ret;
    297}
    298
    299/*
    300	Callback for completed playback URB.
    301*/
    302static void audio_out_callback(struct urb *urb)
    303{
    304	int i, index, length = 0, shutdown = 0;
    305	unsigned long flags;
    306	struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context;
    307	struct snd_pcm_substream *substream =
    308	    get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK);
    309	const int bytes_per_frame =
    310		line6pcm->properties->bytes_per_channel *
    311		line6pcm->properties->playback_hw.channels_max;
    312
    313#if USE_CLEAR_BUFFER_WORKAROUND
    314	memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);
    315#endif
    316
    317	line6pcm->out.last_frame = urb->start_frame;
    318
    319	/* find index of URB */
    320	for (index = 0; index < line6pcm->line6->iso_buffers; index++)
    321		if (urb == line6pcm->out.urbs[index])
    322			break;
    323
    324	if (index >= line6pcm->line6->iso_buffers)
    325		return;		/* URB has been unlinked asynchronously */
    326
    327	for (i = 0; i < LINE6_ISO_PACKETS; i++)
    328		length += urb->iso_frame_desc[i].length;
    329
    330	spin_lock_irqsave(&line6pcm->out.lock, flags);
    331
    332	if (test_bit(LINE6_STREAM_PCM, &line6pcm->out.running)) {
    333		struct snd_pcm_runtime *runtime = substream->runtime;
    334
    335		line6pcm->out.pos_done +=
    336		    length / bytes_per_frame;
    337
    338		if (line6pcm->out.pos_done >= runtime->buffer_size)
    339			line6pcm->out.pos_done -= runtime->buffer_size;
    340	}
    341
    342	clear_bit(index, &line6pcm->out.active_urbs);
    343
    344	for (i = 0; i < LINE6_ISO_PACKETS; i++)
    345		if (urb->iso_frame_desc[i].status == -EXDEV) {
    346			shutdown = 1;
    347			break;
    348		}
    349
    350	if (test_and_clear_bit(index, &line6pcm->out.unlink_urbs))
    351		shutdown = 1;
    352
    353	if (!shutdown) {
    354		submit_audio_out_urb(line6pcm);
    355
    356		if (test_bit(LINE6_STREAM_PCM, &line6pcm->out.running)) {
    357			line6pcm->out.bytes += length;
    358			if (line6pcm->out.bytes >= line6pcm->out.period) {
    359				line6pcm->out.bytes %= line6pcm->out.period;
    360				spin_unlock(&line6pcm->out.lock);
    361				snd_pcm_period_elapsed(substream);
    362				spin_lock(&line6pcm->out.lock);
    363			}
    364		}
    365	}
    366	spin_unlock_irqrestore(&line6pcm->out.lock, flags);
    367}
    368
    369/* open playback callback */
    370static int snd_line6_playback_open(struct snd_pcm_substream *substream)
    371{
    372	int err;
    373	struct snd_pcm_runtime *runtime = substream->runtime;
    374	struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
    375
    376	err = snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
    377					    &line6pcm->properties->rates);
    378	if (err < 0)
    379		return err;
    380
    381	runtime->hw = line6pcm->properties->playback_hw;
    382	return 0;
    383}
    384
    385/* close playback callback */
    386static int snd_line6_playback_close(struct snd_pcm_substream *substream)
    387{
    388	return 0;
    389}
    390
    391/* playback operators */
    392const struct snd_pcm_ops snd_line6_playback_ops = {
    393	.open = snd_line6_playback_open,
    394	.close = snd_line6_playback_close,
    395	.hw_params = snd_line6_hw_params,
    396	.hw_free = snd_line6_hw_free,
    397	.prepare = snd_line6_prepare,
    398	.trigger = snd_line6_trigger,
    399	.pointer = snd_line6_pointer,
    400};
    401
    402int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm)
    403{
    404	struct usb_line6 *line6 = line6pcm->line6;
    405	int i;
    406
    407	line6pcm->out.urbs = kcalloc(line6->iso_buffers, sizeof(struct urb *),
    408				     GFP_KERNEL);
    409	if (line6pcm->out.urbs == NULL)
    410		return -ENOMEM;
    411
    412	/* create audio URBs and fill in constant values: */
    413	for (i = 0; i < line6->iso_buffers; ++i) {
    414		struct urb *urb;
    415
    416		/* URB for audio out: */
    417		urb = line6pcm->out.urbs[i] =
    418		    usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL);
    419
    420		if (urb == NULL)
    421			return -ENOMEM;
    422
    423		urb->dev = line6->usbdev;
    424		urb->pipe =
    425		    usb_sndisocpipe(line6->usbdev,
    426				    line6->properties->ep_audio_w &
    427				    USB_ENDPOINT_NUMBER_MASK);
    428		urb->transfer_flags = URB_ISO_ASAP;
    429		urb->start_frame = -1;
    430		urb->number_of_packets = LINE6_ISO_PACKETS;
    431		urb->interval = LINE6_ISO_INTERVAL;
    432		urb->error_count = 0;
    433		urb->complete = audio_out_callback;
    434		if (usb_urb_ep_type_check(urb))
    435			return -EINVAL;
    436	}
    437
    438	return 0;
    439}