diff options
Diffstat (limited to 'sound/firewire/oxfw')
-rw-r--r-- | sound/firewire/oxfw/oxfw-midi.c | 24 | ||||
-rw-r--r-- | sound/firewire/oxfw/oxfw-pcm.c | 34 | ||||
-rw-r--r-- | sound/firewire/oxfw/oxfw-stream.c | 351 | ||||
-rw-r--r-- | sound/firewire/oxfw/oxfw.c | 17 | ||||
-rw-r--r-- | sound/firewire/oxfw/oxfw.h | 22 |
5 files changed, 252 insertions, 196 deletions
diff --git a/sound/firewire/oxfw/oxfw-midi.c b/sound/firewire/oxfw/oxfw-midi.c index cbce01308bd1..9bdec08cb8ea 100644 --- a/sound/firewire/oxfw/oxfw-midi.c +++ b/sound/firewire/oxfw/oxfw-midi.c @@ -18,8 +18,13 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream) mutex_lock(&oxfw->mutex); - oxfw->capture_substreams++; - err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->tx_stream, 0, 0); + err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream, 0, 0); + if (err >= 0) { + ++oxfw->substreams_count; + err = snd_oxfw_stream_start_duplex(oxfw); + if (err < 0) + --oxfw->substreams_count; + } mutex_unlock(&oxfw->mutex); @@ -40,8 +45,11 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream) mutex_lock(&oxfw->mutex); - oxfw->playback_substreams++; - err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->rx_stream, 0, 0); + err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream, 0, 0); + if (err >= 0) { + ++oxfw->substreams_count; + err = snd_oxfw_stream_start_duplex(oxfw); + } mutex_unlock(&oxfw->mutex); @@ -57,8 +65,8 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream) mutex_lock(&oxfw->mutex); - oxfw->capture_substreams--; - snd_oxfw_stream_stop_simplex(oxfw, &oxfw->tx_stream); + --oxfw->substreams_count; + snd_oxfw_stream_stop_duplex(oxfw); mutex_unlock(&oxfw->mutex); @@ -72,8 +80,8 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream) mutex_lock(&oxfw->mutex); - oxfw->playback_substreams--; - snd_oxfw_stream_stop_simplex(oxfw, &oxfw->rx_stream); + --oxfw->substreams_count; + snd_oxfw_stream_stop_duplex(oxfw); mutex_unlock(&oxfw->mutex); diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c index 94f367cdfdf3..9ea39348cdf5 100644 --- a/sound/firewire/oxfw/oxfw-pcm.c +++ b/sound/firewire/oxfw/oxfw-pcm.c @@ -219,12 +219,18 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream, return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { + unsigned int rate = params_rate(hw_params); + unsigned int channels = params_channels(hw_params); + mutex_lock(&oxfw->mutex); - oxfw->capture_substreams++; + err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream, + rate, channels); + if (err >= 0) + ++oxfw->substreams_count; mutex_unlock(&oxfw->mutex); } - return 0; + return err; } static int pcm_playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) @@ -238,8 +244,14 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream, return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { + unsigned int rate = params_rate(hw_params); + unsigned int channels = params_channels(hw_params); + mutex_lock(&oxfw->mutex); - oxfw->playback_substreams++; + err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream, + rate, channels); + if (err >= 0) + ++oxfw->substreams_count; mutex_unlock(&oxfw->mutex); } @@ -253,9 +265,9 @@ static int pcm_capture_hw_free(struct snd_pcm_substream *substream) mutex_lock(&oxfw->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) - oxfw->capture_substreams--; + --oxfw->substreams_count; - snd_oxfw_stream_stop_simplex(oxfw, &oxfw->tx_stream); + snd_oxfw_stream_stop_duplex(oxfw); mutex_unlock(&oxfw->mutex); @@ -268,9 +280,9 @@ static int pcm_playback_hw_free(struct snd_pcm_substream *substream) mutex_lock(&oxfw->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) - oxfw->playback_substreams--; + --oxfw->substreams_count; - snd_oxfw_stream_stop_simplex(oxfw, &oxfw->rx_stream); + snd_oxfw_stream_stop_duplex(oxfw); mutex_unlock(&oxfw->mutex); @@ -280,12 +292,10 @@ static int pcm_playback_hw_free(struct snd_pcm_substream *substream) static int pcm_capture_prepare(struct snd_pcm_substream *substream) { struct snd_oxfw *oxfw = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; int err; mutex_lock(&oxfw->mutex); - err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->tx_stream, - runtime->rate, runtime->channels); + err = snd_oxfw_stream_start_duplex(oxfw); mutex_unlock(&oxfw->mutex); if (err < 0) goto end; @@ -297,12 +307,10 @@ end: static int pcm_playback_prepare(struct snd_pcm_substream *substream) { struct snd_oxfw *oxfw = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; int err; mutex_lock(&oxfw->mutex); - err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->rx_stream, - runtime->rate, runtime->channels); + err = snd_oxfw_stream_start_duplex(oxfw); mutex_unlock(&oxfw->mutex); if (err < 0) goto end; diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index 5ffedb0ade3f..74c972d25c66 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -100,85 +100,34 @@ static int set_stream_format(struct snd_oxfw *oxfw, struct amdtp_stream *s, return 0; } -static void stop_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream) +static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream) { - amdtp_stream_pcm_abort(stream); - amdtp_stream_stop(stream); - - if (stream == &oxfw->tx_stream) - cmp_connection_break(&oxfw->out_conn); - else - cmp_connection_break(&oxfw->in_conn); -} - -static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream, - unsigned int rate, unsigned int pcm_channels) -{ - u8 **formats; struct cmp_connection *conn; - struct snd_oxfw_stream_formation formation; - unsigned int i, midi_ports; int err; - if (stream == &oxfw->rx_stream) { - formats = oxfw->rx_stream_formats; + if (stream == &oxfw->rx_stream) conn = &oxfw->in_conn; - } else { - formats = oxfw->tx_stream_formats; + else conn = &oxfw->out_conn; - } - - /* Get stream format */ - for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { - if (formats[i] == NULL) - break; - - err = snd_oxfw_stream_parse_format(formats[i], &formation); - if (err < 0) - goto end; - if (rate != formation.rate) - continue; - if (pcm_channels == 0 || pcm_channels == formation.pcm) - break; - } - if (i == SND_OXFW_STREAM_FORMAT_ENTRIES) { - err = -EINVAL; - goto end; - } - - pcm_channels = formation.pcm; - midi_ports = formation.midi * 8; - - /* The stream should have one pcm channels at least */ - if (pcm_channels == 0) { - err = -EINVAL; - goto end; - } - err = amdtp_am824_set_parameters(stream, rate, pcm_channels, midi_ports, - false); - if (err < 0) - goto end; - err = cmp_connection_establish(conn, - amdtp_stream_get_max_payload(stream)); + err = cmp_connection_establish(conn); if (err < 0) - goto end; + return err; - err = amdtp_stream_start(stream, - conn->resources.channel, - conn->speed); + err = amdtp_stream_start(stream, conn->resources.channel, conn->speed); if (err < 0) { cmp_connection_break(conn); - goto end; + return err; } - /* Wait first packet */ + // Wait first packet. if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) { - stop_stream(oxfw, stream); - err = -ETIMEDOUT; + amdtp_stream_stop(stream); + cmp_connection_break(conn); + return -ETIMEDOUT; } -end: - return err; + + return 0; } static int check_connection_used_by_others(struct snd_oxfw *oxfw, @@ -205,8 +154,7 @@ static int check_connection_used_by_others(struct snd_oxfw *oxfw, return err; } -int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw, - struct amdtp_stream *stream) +static int init_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream) { struct cmp_connection *conn; enum cmp_direction c_dir; @@ -225,13 +173,12 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw, err = cmp_connection_init(conn, oxfw->unit, c_dir, 0); if (err < 0) - goto end; + return err; err = amdtp_am824_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING); if (err < 0) { - amdtp_stream_destroy(stream); cmp_connection_destroy(conn); - goto end; + return err; } /* @@ -245,115 +192,195 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw, if (oxfw->wrong_dbs) oxfw->tx_stream.flags |= CIP_WRONG_DBS; } -end: - return err; + + return 0; } -int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw, - struct amdtp_stream *stream, - unsigned int rate, unsigned int pcm_channels) +static int keep_resources(struct snd_oxfw *oxfw, struct amdtp_stream *stream) { - struct amdtp_stream *opposite; - struct snd_oxfw_stream_formation formation; enum avc_general_plug_dir dir; - unsigned int substreams, opposite_substreams; - int err = 0; + u8 **formats; + struct snd_oxfw_stream_formation formation; + struct cmp_connection *conn; + int i; + int err; - if (stream == &oxfw->tx_stream) { - substreams = oxfw->capture_substreams; - opposite = &oxfw->rx_stream; - opposite_substreams = oxfw->playback_substreams; - dir = AVC_GENERAL_PLUG_DIR_OUT; + if (stream == &oxfw->rx_stream) { + dir = AVC_GENERAL_PLUG_DIR_IN; + formats = oxfw->rx_stream_formats; + conn = &oxfw->in_conn; } else { - substreams = oxfw->playback_substreams; - opposite_substreams = oxfw->capture_substreams; + dir = AVC_GENERAL_PLUG_DIR_OUT; + formats = oxfw->tx_stream_formats; + conn = &oxfw->out_conn; + } - if (oxfw->has_output) - opposite = &oxfw->rx_stream; - else - opposite = NULL; + err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation); + if (err < 0) + return err; - dir = AVC_GENERAL_PLUG_DIR_IN; + for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { + struct snd_oxfw_stream_formation fmt; + + if (formats[i] == NULL) + break; + + err = snd_oxfw_stream_parse_format(formats[i], &fmt); + if (err < 0) + return err; + + if (fmt.rate == formation.rate && fmt.pcm == formation.pcm && + fmt.midi == formation.midi) + break; } + if (i == SND_OXFW_STREAM_FORMAT_ENTRIES) + return -EINVAL; - if (substreams == 0) - goto end; + // The stream should have one pcm channels at least. + if (formation.pcm == 0) + return -EINVAL; - /* - * Considering JACK/FFADO streaming: - * TODO: This can be removed hwdep functionality becomes popular. - */ - err = check_connection_used_by_others(oxfw, stream); + err = amdtp_am824_set_parameters(stream, formation.rate, formation.pcm, + formation.midi * 8, false); if (err < 0) - goto end; + return err; - /* packet queueing error */ - if (amdtp_streaming_error(stream)) - stop_stream(oxfw, stream); + return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream)); +} + +int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw, + struct amdtp_stream *stream, + unsigned int rate, unsigned int pcm_channels) +{ + struct snd_oxfw_stream_formation formation; + enum avc_general_plug_dir dir; + int err; + + // Considering JACK/FFADO streaming: + // TODO: This can be removed hwdep functionality becomes popular. + err = check_connection_used_by_others(oxfw, &oxfw->rx_stream); + if (err < 0) + return err; + if (oxfw->has_output) { + err = check_connection_used_by_others(oxfw, &oxfw->tx_stream); + if (err < 0) + return err; + } + + if (stream == &oxfw->tx_stream) + dir = AVC_GENERAL_PLUG_DIR_OUT; + else + dir = AVC_GENERAL_PLUG_DIR_IN; err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation); if (err < 0) - goto end; - if (rate == 0) + return err; + if (rate == 0) { rate = formation.rate; - if (pcm_channels == 0) pcm_channels = formation.pcm; + } + if (formation.rate != rate || formation.pcm != pcm_channels) { + amdtp_stream_stop(&oxfw->rx_stream); + cmp_connection_break(&oxfw->in_conn); + cmp_connection_release(&oxfw->in_conn); - if ((formation.rate != rate) || (formation.pcm != pcm_channels)) { - if (opposite != NULL) { - err = check_connection_used_by_others(oxfw, opposite); - if (err < 0) - goto end; - stop_stream(oxfw, opposite); + if (oxfw->has_output) { + amdtp_stream_stop(&oxfw->tx_stream); + cmp_connection_break(&oxfw->out_conn); + cmp_connection_release(&oxfw->out_conn); } - stop_stream(oxfw, stream); + } + if (oxfw->substreams_count == 0 || + formation.rate != rate || formation.pcm != pcm_channels) { err = set_stream_format(oxfw, stream, rate, pcm_channels); if (err < 0) { dev_err(&oxfw->unit->device, "fail to set stream format: %d\n", err); - goto end; + return err; } - /* Start opposite stream if needed. */ - if (opposite && !amdtp_stream_running(opposite) && - (opposite_substreams > 0)) { - err = start_stream(oxfw, opposite, rate, 0); + err = keep_resources(oxfw, &oxfw->rx_stream); + if (err < 0) + return err; + + if (oxfw->has_output) { + err = keep_resources(oxfw, &oxfw->tx_stream); if (err < 0) { - dev_err(&oxfw->unit->device, - "fail to restart stream: %d\n", err); - goto end; + cmp_connection_release(&oxfw->in_conn); + return err; } } } - /* Start requested stream. */ - if (!amdtp_stream_running(stream)) { - err = start_stream(oxfw, stream, rate, pcm_channels); - if (err < 0) + return 0; +} + +int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw) +{ + int err; + + if (oxfw->substreams_count == 0) + return -EIO; + + if (amdtp_streaming_error(&oxfw->rx_stream) || + amdtp_streaming_error(&oxfw->tx_stream)) { + amdtp_stream_stop(&oxfw->rx_stream); + cmp_connection_break(&oxfw->in_conn); + + if (oxfw->has_output) { + amdtp_stream_stop(&oxfw->tx_stream); + cmp_connection_break(&oxfw->out_conn); + } + } + + if (!amdtp_stream_running(&oxfw->rx_stream)) { + err = start_stream(oxfw, &oxfw->rx_stream); + if (err < 0) { dev_err(&oxfw->unit->device, - "fail to start stream: %d\n", err); + "fail to start rx stream: %d\n", err); + goto error; + } + } + + if (oxfw->has_output) { + if (!amdtp_stream_running(&oxfw->tx_stream)) { + err = start_stream(oxfw, &oxfw->tx_stream); + if (err < 0) { + dev_err(&oxfw->unit->device, + "fail to start tx stream: %d\n", err); + goto error; + } + } + } + + return 0; +error: + amdtp_stream_stop(&oxfw->rx_stream); + cmp_connection_break(&oxfw->in_conn); + if (oxfw->has_output) { + amdtp_stream_stop(&oxfw->tx_stream); + cmp_connection_break(&oxfw->out_conn); } -end: return err; } -void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw, - struct amdtp_stream *stream) +void snd_oxfw_stream_stop_duplex(struct snd_oxfw *oxfw) { - if (((stream == &oxfw->tx_stream) && (oxfw->capture_substreams > 0)) || - ((stream == &oxfw->rx_stream) && (oxfw->playback_substreams > 0))) - return; + if (oxfw->substreams_count == 0) { + amdtp_stream_stop(&oxfw->rx_stream); + cmp_connection_break(&oxfw->in_conn); + cmp_connection_release(&oxfw->in_conn); - stop_stream(oxfw, stream); + if (oxfw->has_output) { + amdtp_stream_stop(&oxfw->tx_stream); + cmp_connection_break(&oxfw->out_conn); + cmp_connection_release(&oxfw->out_conn); + } + } } -/* - * This function should be called before starting the stream or after stopping - * the streams. - */ -void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw, - struct amdtp_stream *stream) +static void destroy_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream) { struct cmp_connection *conn; @@ -366,20 +393,48 @@ void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw, cmp_connection_destroy(conn); } -void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw, - struct amdtp_stream *stream) +int snd_oxfw_stream_init_duplex(struct snd_oxfw *oxfw) { - struct cmp_connection *conn; + int err; - if (stream == &oxfw->tx_stream) - conn = &oxfw->out_conn; - else - conn = &oxfw->in_conn; + err = init_stream(oxfw, &oxfw->rx_stream); + if (err < 0) + return err; - if (cmp_connection_update(conn) < 0) - stop_stream(oxfw, stream); - else - amdtp_stream_update(stream); + if (oxfw->has_output) { + err = init_stream(oxfw, &oxfw->tx_stream); + if (err < 0) { + destroy_stream(oxfw, &oxfw->rx_stream); + return err; + } + } + + return 0; +} + +// This function should be called before starting the stream or after stopping +// the streams. +void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw) +{ + destroy_stream(oxfw, &oxfw->rx_stream); + + if (oxfw->has_output) + destroy_stream(oxfw, &oxfw->tx_stream); +} + +void snd_oxfw_stream_update_duplex(struct snd_oxfw *oxfw) +{ + amdtp_stream_stop(&oxfw->rx_stream); + cmp_connection_break(&oxfw->in_conn); + + amdtp_stream_pcm_abort(&oxfw->rx_stream); + + if (oxfw->has_output) { + amdtp_stream_stop(&oxfw->tx_stream); + cmp_connection_break(&oxfw->out_conn); + + amdtp_stream_pcm_abort(&oxfw->tx_stream); + } } int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw, diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c index 9fd145cc4b07..fb6df3fc018e 100644 --- a/sound/firewire/oxfw/oxfw.c +++ b/sound/firewire/oxfw/oxfw.c @@ -118,9 +118,7 @@ static void oxfw_card_free(struct snd_card *card) { struct snd_oxfw *oxfw = card->private_data; - snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream); - if (oxfw->has_output) - snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream); + snd_oxfw_stream_destroy_duplex(oxfw); } static int detect_quirks(struct snd_oxfw *oxfw) @@ -208,14 +206,9 @@ static void do_registration(struct work_struct *work) if (err < 0) goto error; - err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream); + err = snd_oxfw_stream_init_duplex(oxfw); if (err < 0) goto error; - if (oxfw->has_output) { - err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->tx_stream); - if (err < 0) - goto error; - } err = snd_oxfw_create_pcm(oxfw); if (err < 0) @@ -282,11 +275,7 @@ static void oxfw_bus_reset(struct fw_unit *unit) if (oxfw->registered) { mutex_lock(&oxfw->mutex); - - snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream); - if (oxfw->has_output) - snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream); - + snd_oxfw_stream_update_duplex(oxfw); mutex_unlock(&oxfw->mutex); if (oxfw->entry->vendor_id == OUI_STANTON) diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h index 36112850ef92..cb69ab87bb14 100644 --- a/sound/firewire/oxfw/oxfw.h +++ b/sound/firewire/oxfw/oxfw.h @@ -52,8 +52,7 @@ struct snd_oxfw { struct cmp_connection in_conn; struct amdtp_stream tx_stream; struct amdtp_stream rx_stream; - unsigned int capture_substreams; - unsigned int playback_substreams; + unsigned int substreams_count; unsigned int midi_input_ports; unsigned int midi_output_ports; @@ -99,17 +98,14 @@ int avc_general_inquiry_sig_fmt(struct fw_unit *unit, unsigned int rate, enum avc_general_plug_dir dir, unsigned short pid); -int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw, - struct amdtp_stream *stream); -int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw, - struct amdtp_stream *stream, - unsigned int rate, unsigned int pcm_channels); -void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw, - struct amdtp_stream *stream); -void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw, - struct amdtp_stream *stream); -void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw, - struct amdtp_stream *stream); +int snd_oxfw_stream_init_duplex(struct snd_oxfw *oxfw); +int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw, + struct amdtp_stream *stream, + unsigned int rate, unsigned int pcm_channels); +int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw); +void snd_oxfw_stream_stop_duplex(struct snd_oxfw *oxfw); +void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw); +void snd_oxfw_stream_update_duplex(struct snd_oxfw *oxfw); struct snd_oxfw_stream_formation { unsigned int rate; |