Linux Audio

Check our new training course

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