summaryrefslogtreecommitdiffstats
path: root/sound/usb/line6/pcm.h
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2015-01-27 15:24:09 +0100
committerTakashi Iwai <tiwai@suse.de>2015-01-28 07:22:36 +0100
commit63e20df1e5b2ef8d871ecbdb6c038d554ed1ca74 (patch)
treeaff5e07f44901d5800eabd4fddc501c20cf9f1af /sound/usb/line6/pcm.h
parentf2bb614bb6c7f5245521195f144272ef93d9f086 (diff)
downloadlinux-63e20df1e5b2ef8d871ecbdb6c038d554ed1ca74.tar.bz2
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 <chris@rorvick.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/usb/line6/pcm.h')
-rw-r--r--sound/usb/line6/pcm.h144
1 files changed, 40 insertions, 104 deletions
diff --git a/sound/usb/line6/pcm.h b/sound/usb/line6/pcm.h
index 4c74f4e85074..66f603dfa34e 100644
--- a/sound/usb/line6/pcm.h
+++ b/sound/usb/line6/pcm.h
@@ -54,109 +54,33 @@
However, from the device's point of view, there is just a single
capture and playback stream, which must be shared between these
subsystems. It is therefore necessary to maintain the state of the
- subsystems with respect to PCM usage. We define several constants of
- the form LINE6_BIT_PCM_<subsystem>_<direction>_<resource> with the
- following meanings:
- *) <subsystem> is one of
- -) ALSA: PCM playback and capture via ALSA
- -) MONITOR: software monitoring
- -) IMPULSE: optional impulse response measurement
- *) <direction> is one of
- -) PLAYBACK: audio output (from host to device)
- -) CAPTURE: audio input (from device to host)
- *) <resource> is one of
- -) BUFFER: buffer required by PCM data stream
- -) STREAM: actual PCM data stream
-
- The subsystems call line6_pcm_acquire() to acquire the (shared)
- resources needed for a particular operation (e.g., allocate the buffer
- for ALSA playback or start the capture stream for software monitoring).
- When a resource is no longer needed, it is released by calling
- line6_pcm_release(). Buffer allocation and stream startup are handled
- separately to allow the ALSA kernel driver to perform them at
- appropriate places (since the callback which starts a PCM stream is not
- allowed to sleep).
+ subsystems with respect to PCM usage.
+
+ We define two bit flags, "opened" and "running", for each playback
+ or capture stream. Both can contain the bit flag corresponding to
+ LINE6_STREAM_* type,
+ LINE6_STREAM_PCM = ALSA PCM playback or capture
+ LINE6_STREAM_MONITOR = software monitoring
+ IMPULSE = optional impulse response measurement
+ The opened flag indicates whether the buffer is allocated while
+ the running flag indicates whether the stream is running.
+
+ For monitor or impulse operations, the driver needs to call
+ snd_line6_duplex_acquire() or snd_line6_duplex_release() with the
+ appropriate LINE6_STREAM_* flag.
*/
+
+/* stream types */
+enum {
+ LINE6_STREAM_PCM,
+ LINE6_STREAM_MONITOR,
+ LINE6_STREAM_IMPULSE,
+};
+
+/* misc bit flags for PCM operation */
enum {
- /* individual bit indices: */
- LINE6_INDEX_PCM_ALSA_PLAYBACK_BUFFER,
- LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM,
- LINE6_INDEX_PCM_ALSA_CAPTURE_BUFFER,
- LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM,
- LINE6_INDEX_PCM_MONITOR_PLAYBACK_BUFFER,
- LINE6_INDEX_PCM_MONITOR_PLAYBACK_STREAM,
- LINE6_INDEX_PCM_MONITOR_CAPTURE_BUFFER,
- LINE6_INDEX_PCM_MONITOR_CAPTURE_STREAM,
- LINE6_INDEX_PCM_IMPULSE_PLAYBACK_BUFFER,
- LINE6_INDEX_PCM_IMPULSE_PLAYBACK_STREAM,
- LINE6_INDEX_PCM_IMPULSE_CAPTURE_BUFFER,
- LINE6_INDEX_PCM_IMPULSE_CAPTURE_STREAM,
- LINE6_INDEX_PAUSE_PLAYBACK,
- LINE6_INDEX_PREPARED,
-
-#define LINE6_BIT(x) LINE6_BIT_ ## x = 1 << LINE6_INDEX_ ## x
-
- /* individual bit masks: */
- LINE6_BIT(PCM_ALSA_PLAYBACK_BUFFER),
- LINE6_BIT(PCM_ALSA_PLAYBACK_STREAM),
- LINE6_BIT(PCM_ALSA_CAPTURE_BUFFER),
- LINE6_BIT(PCM_ALSA_CAPTURE_STREAM),
- LINE6_BIT(PCM_MONITOR_PLAYBACK_BUFFER),
- LINE6_BIT(PCM_MONITOR_PLAYBACK_STREAM),
- LINE6_BIT(PCM_MONITOR_CAPTURE_BUFFER),
- LINE6_BIT(PCM_MONITOR_CAPTURE_STREAM),
- LINE6_BIT(PCM_IMPULSE_PLAYBACK_BUFFER),
- LINE6_BIT(PCM_IMPULSE_PLAYBACK_STREAM),
- LINE6_BIT(PCM_IMPULSE_CAPTURE_BUFFER),
- LINE6_BIT(PCM_IMPULSE_CAPTURE_STREAM),
- LINE6_BIT(PAUSE_PLAYBACK),
- LINE6_BIT(PREPARED),
-
- /* combined bit masks (by operation): */
- LINE6_BITS_PCM_ALSA_BUFFER =
- LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER |
- LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER,
-
- LINE6_BITS_PCM_ALSA_STREAM =
- LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM |
- LINE6_BIT_PCM_ALSA_CAPTURE_STREAM,
-
- LINE6_BITS_PCM_MONITOR =
- LINE6_BIT_PCM_MONITOR_PLAYBACK_BUFFER |
- LINE6_BIT_PCM_MONITOR_PLAYBACK_STREAM |
- LINE6_BIT_PCM_MONITOR_CAPTURE_BUFFER |
- LINE6_BIT_PCM_MONITOR_CAPTURE_STREAM,
-
- LINE6_BITS_PCM_IMPULSE =
- LINE6_BIT_PCM_IMPULSE_PLAYBACK_BUFFER |
- LINE6_BIT_PCM_IMPULSE_PLAYBACK_STREAM |
- LINE6_BIT_PCM_IMPULSE_CAPTURE_BUFFER |
- LINE6_BIT_PCM_IMPULSE_CAPTURE_STREAM,
-
- /* combined bit masks (by direction): */
- LINE6_BITS_PLAYBACK_BUFFER =
- LINE6_BIT_PCM_IMPULSE_PLAYBACK_BUFFER |
- LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER |
- LINE6_BIT_PCM_MONITOR_PLAYBACK_BUFFER,
-
- LINE6_BITS_PLAYBACK_STREAM =
- LINE6_BIT_PCM_IMPULSE_PLAYBACK_STREAM |
- LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM |
- LINE6_BIT_PCM_MONITOR_PLAYBACK_STREAM,
-
- LINE6_BITS_CAPTURE_BUFFER =
- LINE6_BIT_PCM_IMPULSE_CAPTURE_BUFFER |
- LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER |
- LINE6_BIT_PCM_MONITOR_CAPTURE_BUFFER,
-
- LINE6_BITS_CAPTURE_STREAM =
- LINE6_BIT_PCM_IMPULSE_CAPTURE_STREAM |
- LINE6_BIT_PCM_ALSA_CAPTURE_STREAM |
- LINE6_BIT_PCM_MONITOR_CAPTURE_STREAM,
-
- LINE6_BITS_STREAM =
- LINE6_BITS_PLAYBACK_STREAM |
- LINE6_BITS_CAPTURE_STREAM
+ LINE6_FLAG_PAUSE_PLAYBACK,
+ LINE6_FLAG_PREPARED,
};
struct line6_pcm_properties {
@@ -205,6 +129,12 @@ struct line6_pcm_stream {
*/
spinlock_t lock;
+ /* Bit flags for operational stream types */
+ unsigned long opened;
+
+ /* Bit flags for running stream types */
+ unsigned long running;
+
int last_frame;
};
@@ -224,6 +154,9 @@ struct snd_line6_pcm {
*/
struct snd_pcm *pcm;
+ /* protection to state changes of in/out streams */
+ struct mutex state_mutex;
+
/* Capture and playback streams */
struct line6_pcm_stream in;
struct line6_pcm_stream out;
@@ -269,7 +202,7 @@ struct snd_line6_pcm {
int impulse_count;
/**
- Several status bits (see LINE6_BIT_*).
+ Several status bits (see LINE6_FLAG_*).
*/
unsigned long flags;
};
@@ -278,8 +211,11 @@ extern int line6_init_pcm(struct usb_line6 *line6,
struct line6_pcm_properties *properties);
extern int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd);
extern int snd_line6_prepare(struct snd_pcm_substream *substream);
+extern int snd_line6_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params);
+extern int snd_line6_hw_free(struct snd_pcm_substream *substream);
extern void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm);
-extern int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int channels);
-extern int line6_pcm_release(struct snd_line6_pcm *line6pcm, int channels);
+extern int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type);
+extern void line6_pcm_release(struct snd_line6_pcm *line6pcm, int type);
#endif