Skip to content

Commit 63e20df

Browse files
committed
ALSA: line6: Reorganize PCM stream handling
The current code deals with the stream start / stop solely via line6_pcm_acquire() and line6_pcm_release(). This was (supposedly) intended to avoid the races, but it doesn't work as expected. The concurrent acquire and release calls can be performed without proper protections, thus this might result in memory corruption. Furthermore, we can't take a mutex to protect the whole function because it can be called from the PCM trigger callback that is an atomic context. Also spinlock isn't appropriate because the function allocates with kmalloc with GFP_KERNEL. That is, these function just lead to singular problems. This is an attempt to reduce the existing races. First off, separate both the stream buffer management and the stream URB management. The former is protected via a newly introduced state_mutex while the latter is protected via each line6_pcm_stream lock. Secondly, the stream state are now managed in opened and running bit flags of each line6_pcm_stream. Not only this a bit clearer than previous combined bit flags, this also gives a better abstraction. These rewrites allows us to make common hw_params and hw_free callbacks for both playback and capture directions. For the monitor and impulse operations, still line6_pcm_acquire() and line6_pcm_release() are used. They call internally the corresponding functions for both playback and capture streams with proper lock or mutex. Unlike the previous versions, these function don't take the bit masks but the only single type value. Also they are supposed to be applied only as duplex operations. Tested-by: Chris Rorvick <[email protected]> Signed-off-by: Takashi Iwai <[email protected]>
1 parent f2bb614 commit 63e20df

File tree

7 files changed

+252
-425
lines changed

7 files changed

+252
-425
lines changed

sound/usb/line6/capture.c

+10-78
Original file line numberDiff line numberDiff line change
@@ -67,20 +67,18 @@ static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm)
6767

6868
/*
6969
Submit all currently available capture URBs.
70+
must be called in line6pcm->in.lock context
7071
*/
7172
int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm)
7273
{
73-
unsigned long flags;
7474
int ret = 0, i;
7575

76-
spin_lock_irqsave(&line6pcm->in.lock, flags);
7776
for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
7877
ret = submit_audio_in_urb(line6pcm);
7978
if (ret < 0)
8079
break;
8180
}
8281

83-
spin_unlock_irqrestore(&line6pcm->in.lock, flags);
8482
return ret;
8583
}
8684

@@ -187,10 +185,10 @@ static void audio_in_callback(struct urb *urb)
187185
line6pcm->prev_fbuf = fbuf;
188186
line6pcm->prev_fsize = fsize;
189187

190-
if (!(line6pcm->flags & LINE6_BITS_PCM_IMPULSE))
191-
if (test_bit(LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM,
192-
&line6pcm->flags) && (fsize > 0))
193-
line6_capture_copy(line6pcm, fbuf, fsize);
188+
if (!test_bit(LINE6_STREAM_IMPULSE, &line6pcm->in.running) &&
189+
test_bit(LINE6_STREAM_PCM, &line6pcm->in.running) &&
190+
fsize > 0)
191+
line6_capture_copy(line6pcm, fbuf, fsize);
194192
}
195193

196194
clear_bit(index, &line6pcm->in.active_urbs);
@@ -201,10 +199,9 @@ static void audio_in_callback(struct urb *urb)
201199
if (!shutdown) {
202200
submit_audio_in_urb(line6pcm);
203201

204-
if (!(line6pcm->flags & LINE6_BITS_PCM_IMPULSE))
205-
if (test_bit(LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM,
206-
&line6pcm->flags))
207-
line6_capture_check_period(line6pcm, length);
202+
if (!test_bit(LINE6_STREAM_IMPULSE, &line6pcm->in.running) &&
203+
test_bit(LINE6_STREAM_PCM, &line6pcm->in.running))
204+
line6_capture_check_period(line6pcm, length);
208205
}
209206

210207
spin_unlock_irqrestore(&line6pcm->in.lock, flags);
@@ -234,71 +231,6 @@ static int snd_line6_capture_close(struct snd_pcm_substream *substream)
234231
return 0;
235232
}
236233

237-
/* hw_params capture callback */
238-
static int snd_line6_capture_hw_params(struct snd_pcm_substream *substream,
239-
struct snd_pcm_hw_params *hw_params)
240-
{
241-
int ret;
242-
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
243-
244-
ret = line6_pcm_acquire(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER);
245-
246-
if (ret < 0)
247-
return ret;
248-
249-
ret = snd_pcm_lib_malloc_pages(substream,
250-
params_buffer_bytes(hw_params));
251-
if (ret < 0) {
252-
line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER);
253-
return ret;
254-
}
255-
256-
line6pcm->in.period = params_period_bytes(hw_params);
257-
return 0;
258-
}
259-
260-
/* hw_free capture callback */
261-
static int snd_line6_capture_hw_free(struct snd_pcm_substream *substream)
262-
{
263-
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
264-
265-
line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER);
266-
return snd_pcm_lib_free_pages(substream);
267-
}
268-
269-
/* trigger callback */
270-
int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd)
271-
{
272-
int err;
273-
274-
switch (cmd) {
275-
case SNDRV_PCM_TRIGGER_START:
276-
case SNDRV_PCM_TRIGGER_RESUME:
277-
err = line6_pcm_acquire(line6pcm,
278-
LINE6_BIT_PCM_ALSA_CAPTURE_STREAM);
279-
280-
if (err < 0)
281-
return err;
282-
283-
break;
284-
285-
case SNDRV_PCM_TRIGGER_STOP:
286-
case SNDRV_PCM_TRIGGER_SUSPEND:
287-
err = line6_pcm_release(line6pcm,
288-
LINE6_BIT_PCM_ALSA_CAPTURE_STREAM);
289-
290-
if (err < 0)
291-
return err;
292-
293-
break;
294-
295-
default:
296-
return -EINVAL;
297-
}
298-
299-
return 0;
300-
}
301-
302234
/* capture pointer callback */
303235
static snd_pcm_uframes_t
304236
snd_line6_capture_pointer(struct snd_pcm_substream *substream)
@@ -313,8 +245,8 @@ struct snd_pcm_ops snd_line6_capture_ops = {
313245
.open = snd_line6_capture_open,
314246
.close = snd_line6_capture_close,
315247
.ioctl = snd_pcm_lib_ioctl,
316-
.hw_params = snd_line6_capture_hw_params,
317-
.hw_free = snd_line6_capture_hw_free,
248+
.hw_params = snd_line6_hw_params,
249+
.hw_free = snd_line6_hw_free,
318250
.prepare = snd_line6_prepare,
319251
.trigger = snd_line6_trigger,
320252
.pointer = snd_line6_capture_pointer,

sound/usb/line6/capture.h

-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,5 @@ extern void line6_capture_check_period(struct snd_line6_pcm *line6pcm,
2525
int length);
2626
extern int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm);
2727
extern int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm);
28-
extern int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd);
2928

3029
#endif

0 commit comments

Comments
 (0)