brintos

brintos / linux-shallow public Read only

0
0
Text · 7.4 KiB · 84a9b7b Raw
295 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Line 6 Linux USB driver4 *5 * Copyright (C) 2004-2010 Markus Grabner (line6@grabner-graz.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 17/*18	Find a free URB and submit it.19	must be called in line6pcm->in.lock context20*/21static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm)22{23	int index;24	int i, urb_size;25	int ret;26	struct urb *urb_in;27 28	index = find_first_zero_bit(&line6pcm->in.active_urbs,29				    line6pcm->line6->iso_buffers);30 31	if (index < 0 || index >= line6pcm->line6->iso_buffers) {32		dev_err(line6pcm->line6->ifcdev, "no free URB found\n");33		return -EINVAL;34	}35 36	urb_in = line6pcm->in.urbs[index];37	urb_size = 0;38 39	for (i = 0; i < LINE6_ISO_PACKETS; ++i) {40		struct usb_iso_packet_descriptor *fin =41		    &urb_in->iso_frame_desc[i];42		fin->offset = urb_size;43		fin->length = line6pcm->max_packet_size_in;44		urb_size += line6pcm->max_packet_size_in;45	}46 47	urb_in->transfer_buffer =48	    line6pcm->in.buffer +49	    index * LINE6_ISO_PACKETS * line6pcm->max_packet_size_in;50	urb_in->transfer_buffer_length = urb_size;51	urb_in->context = line6pcm;52 53	ret = usb_submit_urb(urb_in, GFP_ATOMIC);54 55	if (ret == 0)56		set_bit(index, &line6pcm->in.active_urbs);57	else58		dev_err(line6pcm->line6->ifcdev,59			"URB in #%d submission failed (%d)\n", index, ret);60 61	return 0;62}63 64/*65	Submit all currently available capture URBs.66	must be called in line6pcm->in.lock context67*/68int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm)69{70	int ret = 0, i;71 72	for (i = 0; i < line6pcm->line6->iso_buffers; ++i) {73		ret = submit_audio_in_urb(line6pcm);74		if (ret < 0)75			break;76	}77 78	return ret;79}80 81/*82	Copy data into ALSA capture buffer.83*/84void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize)85{86	struct snd_pcm_substream *substream =87	    get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE);88	struct snd_pcm_runtime *runtime = substream->runtime;89	const int bytes_per_frame =90		line6pcm->properties->bytes_per_channel *91		line6pcm->properties->capture_hw.channels_max;92	int frames = fsize / bytes_per_frame;93 94	if (runtime == NULL)95		return;96 97	if (line6pcm->in.pos_done + frames > runtime->buffer_size) {98		/*99		   The transferred area goes over buffer boundary,100		   copy two separate chunks.101		 */102		int len;103 104		len = runtime->buffer_size - line6pcm->in.pos_done;105 106		if (len > 0) {107			memcpy(runtime->dma_area +108			       line6pcm->in.pos_done * bytes_per_frame, fbuf,109			       len * bytes_per_frame);110			memcpy(runtime->dma_area, fbuf + len * bytes_per_frame,111			       (frames - len) * bytes_per_frame);112		} else {113			/* this is somewhat paranoid */114			dev_err(line6pcm->line6->ifcdev,115				"driver bug: len = %d\n", len);116		}117	} else {118		/* copy single chunk */119		memcpy(runtime->dma_area +120		       line6pcm->in.pos_done * bytes_per_frame, fbuf, fsize);121	}122 123	line6pcm->in.pos_done += frames;124	if (line6pcm->in.pos_done >= runtime->buffer_size)125		line6pcm->in.pos_done -= runtime->buffer_size;126}127 128void line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length)129{130	struct snd_pcm_substream *substream =131	    get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE);132 133	line6pcm->in.bytes += length;134	if (line6pcm->in.bytes >= line6pcm->in.period) {135		line6pcm->in.bytes %= line6pcm->in.period;136		spin_unlock(&line6pcm->in.lock);137		snd_pcm_period_elapsed(substream);138		spin_lock(&line6pcm->in.lock);139	}140}141 142/*143 * Callback for completed capture URB.144 */145static void audio_in_callback(struct urb *urb)146{147	int i, index, length = 0, shutdown = 0;148	unsigned long flags;149 150	struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context;151 152	line6pcm->in.last_frame = urb->start_frame;153 154	/* find index of URB */155	for (index = 0; index < line6pcm->line6->iso_buffers; ++index)156		if (urb == line6pcm->in.urbs[index])157			break;158 159	spin_lock_irqsave(&line6pcm->in.lock, flags);160 161	for (i = 0; i < LINE6_ISO_PACKETS; ++i) {162		char *fbuf;163		int fsize;164		struct usb_iso_packet_descriptor *fin = &urb->iso_frame_desc[i];165 166		if (fin->status == -EXDEV) {167			shutdown = 1;168			break;169		}170 171		fbuf = urb->transfer_buffer + fin->offset;172		fsize = fin->actual_length;173 174		if (fsize > line6pcm->max_packet_size_in) {175			dev_err(line6pcm->line6->ifcdev,176				"driver and/or device bug: packet too large (%d > %d)\n",177				fsize, line6pcm->max_packet_size_in);178		}179 180		length += fsize;181 182		BUILD_BUG_ON_MSG(LINE6_ISO_PACKETS != 1,183			"The following code assumes LINE6_ISO_PACKETS == 1");184		/* TODO:185		 * Also, if iso_buffers != 2, the prev frame is almost random at186		 * playback side.187		 * This needs to be redesigned. It should be "stable", but we may188		 * experience sync problems on such high-speed configs.189		 */190 191		line6pcm->prev_fbuf = fbuf;192		line6pcm->prev_fsize = fsize /193			(line6pcm->properties->bytes_per_channel *194			line6pcm->properties->capture_hw.channels_max);195 196		if (!test_bit(LINE6_STREAM_IMPULSE, &line6pcm->in.running) &&197		    test_bit(LINE6_STREAM_PCM, &line6pcm->in.running) &&198		    fsize > 0)199			line6_capture_copy(line6pcm, fbuf, fsize);200	}201 202	clear_bit(index, &line6pcm->in.active_urbs);203 204	if (test_and_clear_bit(index, &line6pcm->in.unlink_urbs))205		shutdown = 1;206 207	if (!shutdown) {208		submit_audio_in_urb(line6pcm);209 210		if (!test_bit(LINE6_STREAM_IMPULSE, &line6pcm->in.running) &&211		    test_bit(LINE6_STREAM_PCM, &line6pcm->in.running))212			line6_capture_check_period(line6pcm, length);213	}214 215	spin_unlock_irqrestore(&line6pcm->in.lock, flags);216}217 218/* open capture callback */219static int snd_line6_capture_open(struct snd_pcm_substream *substream)220{221	int err;222	struct snd_pcm_runtime *runtime = substream->runtime;223	struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);224 225	err = snd_pcm_hw_constraint_ratdens(runtime, 0,226					    SNDRV_PCM_HW_PARAM_RATE,227					    &line6pcm->properties->rates);228	if (err < 0)229		return err;230 231	line6_pcm_acquire(line6pcm, LINE6_STREAM_CAPTURE_HELPER, false);232 233	runtime->hw = line6pcm->properties->capture_hw;234	return 0;235}236 237/* close capture callback */238static int snd_line6_capture_close(struct snd_pcm_substream *substream)239{240	struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);241 242	line6_pcm_release(line6pcm, LINE6_STREAM_CAPTURE_HELPER);243	return 0;244}245 246/* capture operators */247const struct snd_pcm_ops snd_line6_capture_ops = {248	.open = snd_line6_capture_open,249	.close = snd_line6_capture_close,250	.hw_params = snd_line6_hw_params,251	.hw_free = snd_line6_hw_free,252	.prepare = snd_line6_prepare,253	.trigger = snd_line6_trigger,254	.pointer = snd_line6_pointer,255};256 257int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm)258{259	struct usb_line6 *line6 = line6pcm->line6;260	int i;261 262	line6pcm->in.urbs = kcalloc(line6->iso_buffers, sizeof(struct urb *),263				    GFP_KERNEL);264	if (line6pcm->in.urbs == NULL)265		return -ENOMEM;266 267	/* create audio URBs and fill in constant values: */268	for (i = 0; i < line6->iso_buffers; ++i) {269		struct urb *urb;270 271		/* URB for audio in: */272		urb = line6pcm->in.urbs[i] =273		    usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL);274 275		if (urb == NULL)276			return -ENOMEM;277 278		urb->dev = line6->usbdev;279		urb->pipe =280		    usb_rcvisocpipe(line6->usbdev,281				    line6->properties->ep_audio_r &282				    USB_ENDPOINT_NUMBER_MASK);283		urb->transfer_flags = URB_ISO_ASAP;284		urb->start_frame = -1;285		urb->number_of_packets = LINE6_ISO_PACKETS;286		urb->interval = LINE6_ISO_INTERVAL;287		urb->error_count = 0;288		urb->complete = audio_in_callback;289		if (usb_urb_ep_type_check(urb))290			return -EINVAL;291	}292 293	return 0;294}295