diff options
Diffstat (limited to 'sound/firewire/bebob')
-rw-r--r-- | sound/firewire/bebob/bebob.h | 5 | ||||
-rw-r--r-- | sound/firewire/bebob/bebob_midi.c | 55 | ||||
-rw-r--r-- | sound/firewire/bebob/bebob_pcm.c | 70 | ||||
-rw-r--r-- | sound/firewire/bebob/bebob_stream.c | 362 |
4 files changed, 217 insertions, 275 deletions
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index af71dac9f084..9e0b689fe34a 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -92,8 +92,6 @@ struct snd_bebob { unsigned int midi_input_ports; unsigned int midi_output_ports; - bool connected; - struct amdtp_stream tx_stream; struct amdtp_stream rx_stream; struct cmp_connection out_conn; @@ -217,7 +215,8 @@ int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob, enum snd_bebob_clock_type *src); int snd_bebob_stream_discover(struct snd_bebob *bebob); int snd_bebob_stream_init_duplex(struct snd_bebob *bebob); -int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate); +int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate); +int snd_bebob_stream_start_duplex(struct snd_bebob *bebob); void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob); void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob); diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c index c54ac42622ad..4d8805fa8a00 100644 --- a/sound/firewire/bebob/bebob_midi.c +++ b/sound/firewire/bebob/bebob_midi.c @@ -7,58 +7,31 @@ #include "bebob.h" -static int midi_capture_open(struct snd_rawmidi_substream *substream) +static int midi_open(struct snd_rawmidi_substream *substream) { struct snd_bebob *bebob = substream->rmidi->private_data; int err; err = snd_bebob_stream_lock_try(bebob); if (err < 0) - goto end; + return err; mutex_lock(&bebob->mutex); - bebob->substreams_counter++; - err = snd_bebob_stream_start_duplex(bebob, 0); + err = snd_bebob_stream_reserve_duplex(bebob, 0); + if (err >= 0) { + ++bebob->substreams_counter; + err = snd_bebob_stream_start_duplex(bebob); + if (err < 0) + --bebob->substreams_counter; + } mutex_unlock(&bebob->mutex); if (err < 0) snd_bebob_stream_lock_release(bebob); -end: - return err; -} - -static int midi_playback_open(struct snd_rawmidi_substream *substream) -{ - struct snd_bebob *bebob = substream->rmidi->private_data; - int err; - - err = snd_bebob_stream_lock_try(bebob); - if (err < 0) - goto end; - mutex_lock(&bebob->mutex); - bebob->substreams_counter++; - err = snd_bebob_stream_start_duplex(bebob, 0); - mutex_unlock(&bebob->mutex); - if (err < 0) - snd_bebob_stream_lock_release(bebob); -end: return err; } -static int midi_capture_close(struct snd_rawmidi_substream *substream) -{ - struct snd_bebob *bebob = substream->rmidi->private_data; - - mutex_lock(&bebob->mutex); - bebob->substreams_counter--; - snd_bebob_stream_stop_duplex(bebob); - mutex_unlock(&bebob->mutex); - - snd_bebob_stream_lock_release(bebob); - return 0; -} - -static int midi_playback_close(struct snd_rawmidi_substream *substream) +static int midi_close(struct snd_rawmidi_substream *substream) { struct snd_bebob *bebob = substream->rmidi->private_data; @@ -120,13 +93,13 @@ static void set_midi_substream_names(struct snd_bebob *bebob, int snd_bebob_create_midi_devices(struct snd_bebob *bebob) { static const struct snd_rawmidi_ops capture_ops = { - .open = midi_capture_open, - .close = midi_capture_close, + .open = midi_open, + .close = midi_close, .trigger = midi_capture_trigger, }; static const struct snd_rawmidi_ops playback_ops = { - .open = midi_playback_open, - .close = midi_playback_close, + .open = midi_open, + .close = midi_close, .trigger = midi_playback_trigger, }; struct snd_rawmidi *rmidi; diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c index 2f50ec7b0147..0fb9eed46837 100644 --- a/sound/firewire/bebob/bebob_pcm.c +++ b/sound/firewire/bebob/bebob_pcm.c @@ -184,9 +184,8 @@ pcm_close(struct snd_pcm_substream *substream) return 0; } -static int -pcm_capture_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) +static int pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) { struct snd_bebob *bebob = substream->private_data; int err; @@ -197,62 +196,31 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream, return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { - mutex_lock(&bebob->mutex); - bebob->substreams_counter++; - mutex_unlock(&bebob->mutex); - } + unsigned int rate = params_rate(hw_params); - return 0; -} -static int -pcm_playback_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_bebob *bebob = substream->private_data; - int err; - - err = snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); - if (err < 0) - return err; - - if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&bebob->mutex); - bebob->substreams_counter++; + err = snd_bebob_stream_reserve_duplex(bebob, rate); + if (err >= 0) + ++bebob->substreams_counter; mutex_unlock(&bebob->mutex); } - return 0; + return err; } -static int -pcm_capture_hw_free(struct snd_pcm_substream *substream) +static int pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; - if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) { - mutex_lock(&bebob->mutex); - bebob->substreams_counter--; - mutex_unlock(&bebob->mutex); - } - - snd_bebob_stream_stop_duplex(bebob); + mutex_lock(&bebob->mutex); - return snd_pcm_lib_free_vmalloc_buffer(substream); -} -static int -pcm_playback_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_bebob *bebob = substream->private_data; - - if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) { - mutex_lock(&bebob->mutex); + if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) bebob->substreams_counter--; - mutex_unlock(&bebob->mutex); - } snd_bebob_stream_stop_duplex(bebob); + mutex_unlock(&bebob->mutex); + return snd_pcm_lib_free_vmalloc_buffer(substream); } @@ -260,10 +228,9 @@ static int pcm_capture_prepare(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; int err; - err = snd_bebob_stream_start_duplex(bebob, runtime->rate); + err = snd_bebob_stream_start_duplex(bebob); if (err >= 0) amdtp_stream_pcm_prepare(&bebob->tx_stream); @@ -273,10 +240,9 @@ static int pcm_playback_prepare(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; int err; - err = snd_bebob_stream_start_duplex(bebob, runtime->rate); + err = snd_bebob_stream_start_duplex(bebob); if (err >= 0) amdtp_stream_pcm_prepare(&bebob->rx_stream); @@ -353,8 +319,8 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob) .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_capture_hw_params, - .hw_free = pcm_capture_hw_free, + .hw_params = pcm_hw_params, + .hw_free = pcm_hw_free, .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, @@ -365,8 +331,8 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob) .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_playback_hw_params, - .hw_free = pcm_playback_hw_free, + .hw_params = pcm_hw_params, + .hw_free = pcm_hw_free, .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 0c93a825cb98..334dc7c96e1d 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -376,24 +376,6 @@ end: } static int -init_both_connections(struct snd_bebob *bebob) -{ - int err; - - err = cmp_connection_init(&bebob->in_conn, - bebob->unit, CMP_INPUT, 0); - if (err < 0) - goto end; - - err = cmp_connection_init(&bebob->out_conn, - bebob->unit, CMP_OUTPUT, 0); - if (err < 0) - cmp_connection_destroy(&bebob->in_conn); -end: - return err; -} - -static int check_connection_used_by_others(struct snd_bebob *bebob, struct amdtp_stream *s) { struct cmp_connection *conn; @@ -417,49 +399,21 @@ check_connection_used_by_others(struct snd_bebob *bebob, struct amdtp_stream *s) return err; } -static int -make_both_connections(struct snd_bebob *bebob, unsigned int rate) +static int make_both_connections(struct snd_bebob *bebob) { - int index, pcm_channels, midi_channels, err = 0; - - if (bebob->connected) - goto end; - - /* confirm params for both streams */ - err = get_formation_index(rate, &index); - if (err < 0) - goto end; - pcm_channels = bebob->tx_stream_formations[index].pcm; - midi_channels = bebob->tx_stream_formations[index].midi; - err = amdtp_am824_set_parameters(&bebob->tx_stream, rate, - pcm_channels, midi_channels * 8, - false); - if (err < 0) - goto end; + int err = 0; - pcm_channels = bebob->rx_stream_formations[index].pcm; - midi_channels = bebob->rx_stream_formations[index].midi; - err = amdtp_am824_set_parameters(&bebob->rx_stream, rate, - pcm_channels, midi_channels * 8, - false); + err = cmp_connection_establish(&bebob->out_conn); if (err < 0) - goto end; + return err; - /* establish connections for both streams */ - err = cmp_connection_establish(&bebob->out_conn, - amdtp_stream_get_max_payload(&bebob->tx_stream)); - if (err < 0) - goto end; - err = cmp_connection_establish(&bebob->in_conn, - amdtp_stream_get_max_payload(&bebob->rx_stream)); + err = cmp_connection_establish(&bebob->in_conn); if (err < 0) { cmp_connection_break(&bebob->out_conn); - goto end; + return err; } - bebob->connected = true; -end: - return err; + return 0; } static void @@ -468,23 +422,13 @@ break_both_connections(struct snd_bebob *bebob) cmp_connection_break(&bebob->in_conn); cmp_connection_break(&bebob->out_conn); - bebob->connected = false; - /* These models seems to be in transition state for a longer time. */ if (bebob->maudio_special_quirk != NULL) msleep(200); } -static void -destroy_both_connections(struct snd_bebob *bebob) -{ - cmp_connection_destroy(&bebob->in_conn); - cmp_connection_destroy(&bebob->out_conn); -} - static int -start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream, - unsigned int rate) +start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream) { struct cmp_connection *conn; int err = 0; @@ -509,190 +453,252 @@ end: return err; } -int snd_bebob_stream_init_duplex(struct snd_bebob *bebob) +static int init_stream(struct snd_bebob *bebob, struct amdtp_stream *stream) { + enum amdtp_stream_direction dir_stream; + struct cmp_connection *conn; + enum cmp_direction dir_conn; int err; - err = init_both_connections(bebob); + if (stream == &bebob->tx_stream) { + dir_stream = AMDTP_IN_STREAM; + conn = &bebob->out_conn; + dir_conn = CMP_OUTPUT; + } else { + dir_stream = AMDTP_OUT_STREAM; + conn = &bebob->in_conn; + dir_conn = CMP_INPUT; + } + + err = cmp_connection_init(conn, bebob->unit, dir_conn, 0); if (err < 0) - goto end; + return err; - err = amdtp_am824_init(&bebob->tx_stream, bebob->unit, - AMDTP_IN_STREAM, CIP_BLOCKING); + err = amdtp_am824_init(stream, bebob->unit, dir_stream, CIP_BLOCKING); if (err < 0) { - amdtp_stream_destroy(&bebob->tx_stream); - destroy_both_connections(bebob); - goto end; + cmp_connection_destroy(conn); + return err; } - /* - * BeBoB v3 transfers packets with these qurks: - * - In the beginning of streaming, the value of dbc is incremented - * even if no data blocks are transferred. - * - The value of dbc is reset suddenly. - */ - if (bebob->version > 2) - bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC | - CIP_SKIP_DBC_ZERO_CHECK; + if (stream == &bebob->tx_stream) { + // BeBoB v3 transfers packets with these qurks: + // - In the beginning of streaming, the value of dbc is + // incremented even if no data blocks are transferred. + // - The value of dbc is reset suddenly. + if (bebob->version > 2) + bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC | + CIP_SKIP_DBC_ZERO_CHECK; + + // At high sampling rate, M-Audio special firmware transmits + // empty packet with the value of dbc incremented by 8 but the + // others are valid to IEC 61883-1. + if (bebob->maudio_special_quirk) + bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC; + } - /* - * At high sampling rate, M-Audio special firmware transmits empty - * packet with the value of dbc incremented by 8 but the others are - * valid to IEC 61883-1. - */ - if (bebob->maudio_special_quirk) - bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC; + return 0; +} + +static void destroy_stream(struct snd_bebob *bebob, struct amdtp_stream *stream) +{ + amdtp_stream_destroy(stream); + + if (stream == &bebob->tx_stream) + cmp_connection_destroy(&bebob->out_conn); + else + cmp_connection_destroy(&bebob->in_conn); +} + +int snd_bebob_stream_init_duplex(struct snd_bebob *bebob) +{ + int err; - err = amdtp_am824_init(&bebob->rx_stream, bebob->unit, - AMDTP_OUT_STREAM, CIP_BLOCKING); + err = init_stream(bebob, &bebob->tx_stream); + if (err < 0) + return err; + + err = init_stream(bebob, &bebob->rx_stream); if (err < 0) { - amdtp_stream_destroy(&bebob->tx_stream); - amdtp_stream_destroy(&bebob->rx_stream); - destroy_both_connections(bebob); + destroy_stream(bebob, &bebob->tx_stream); + return err; } -end: - return err; + + return 0; } -int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) +static int keep_resources(struct snd_bebob *bebob, struct amdtp_stream *stream, + unsigned int rate, unsigned int index) { - const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate; - unsigned int curr_rate; - int err = 0; + struct snd_bebob_stream_formation *formation; + struct cmp_connection *conn; + int err; - /* Need no substreams */ - if (bebob->substreams_counter == 0) - goto end; + if (stream == &bebob->tx_stream) { + formation = bebob->tx_stream_formations + index; + conn = &bebob->out_conn; + } else { + formation = bebob->rx_stream_formations + index; + conn = &bebob->in_conn; + } - /* - * Considering JACK/FFADO streaming: - * TODO: This can be removed hwdep functionality becomes popular. - */ + err = amdtp_am824_set_parameters(stream, rate, formation->pcm, + formation->midi, false); + if (err < 0) + return err; + + return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream)); +} + +int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate) +{ + unsigned int curr_rate; + int err; + + // Considering JACK/FFADO streaming: + // TODO: This can be removed hwdep functionality becomes popular. err = check_connection_used_by_others(bebob, &bebob->rx_stream); if (err < 0) - goto end; + return err; - /* - * packet queueing error or detecting discontinuity - * - * At bus reset, connections should not be broken here. So streams need - * to be re-started. This is a reason to use SKIP_INIT_DBC_CHECK flag. - */ - if (amdtp_streaming_error(&bebob->rx_stream)) - amdtp_stream_stop(&bebob->rx_stream); - if (amdtp_streaming_error(&bebob->tx_stream)) + err = bebob->spec->rate->get(bebob, &curr_rate); + if (err < 0) + return err; + if (rate == 0) + rate = curr_rate; + if (curr_rate != rate) { amdtp_stream_stop(&bebob->tx_stream); - if (!amdtp_stream_running(&bebob->rx_stream) && - !amdtp_stream_running(&bebob->tx_stream)) + amdtp_stream_stop(&bebob->rx_stream); + break_both_connections(bebob); - /* stop streams if rate is different */ - err = rate_spec->get(bebob, &curr_rate); - if (err < 0) { - dev_err(&bebob->unit->device, - "fail to get sampling rate: %d\n", err); - goto end; + cmp_connection_release(&bebob->out_conn); + cmp_connection_release(&bebob->in_conn); } - if (rate == 0) - rate = curr_rate; - if (rate != curr_rate) { + + if (bebob->substreams_counter == 0 || curr_rate != rate) { + unsigned int index; + + // NOTE: + // If establishing connections at first, Yamaha GO46 + // (and maybe Terratec X24) don't generate sound. + // + // For firmware customized by M-Audio, refer to next NOTE. + err = bebob->spec->rate->set(bebob, rate); + if (err < 0) { + dev_err(&bebob->unit->device, + "fail to set sampling rate: %d\n", + err); + return err; + } + + err = get_formation_index(rate, &index); + if (err < 0) + return err; + + err = keep_resources(bebob, &bebob->tx_stream, rate, index); + if (err < 0) + return err; + + err = keep_resources(bebob, &bebob->rx_stream, rate, index); + if (err < 0) { + cmp_connection_release(&bebob->out_conn); + return err; + } + } + + return 0; +} + +int snd_bebob_stream_start_duplex(struct snd_bebob *bebob) +{ + int err; + + // Need no substreams. + if (bebob->substreams_counter == 0) + return -EIO; + + // packet queueing error or detecting discontinuity + if (amdtp_streaming_error(&bebob->rx_stream) || + amdtp_streaming_error(&bebob->tx_stream)) { amdtp_stream_stop(&bebob->rx_stream); amdtp_stream_stop(&bebob->tx_stream); + break_both_connections(bebob); } - /* master should be always running */ if (!amdtp_stream_running(&bebob->rx_stream)) { - /* - * NOTE: - * If establishing connections at first, Yamaha GO46 - * (and maybe Terratec X24) don't generate sound. - * - * For firmware customized by M-Audio, refer to next NOTE. - */ - if (bebob->maudio_special_quirk == NULL) { - err = rate_spec->set(bebob, rate); - if (err < 0) { - dev_err(&bebob->unit->device, - "fail to set sampling rate: %d\n", - err); - goto end; - } + unsigned int curr_rate; + + if (bebob->maudio_special_quirk) { + err = bebob->spec->rate->get(bebob, &curr_rate); + if (err < 0) + return err; } - err = make_both_connections(bebob, rate); + err = make_both_connections(bebob); if (err < 0) - goto end; + return err; - err = start_stream(bebob, &bebob->rx_stream, rate); + err = start_stream(bebob, &bebob->rx_stream); if (err < 0) { dev_err(&bebob->unit->device, "fail to run AMDTP master stream:%d\n", err); - break_both_connections(bebob); - goto end; + goto error; } - /* - * NOTE: - * The firmware customized by M-Audio uses these commands to - * start transmitting stream. This is not usual way. - */ - if (bebob->maudio_special_quirk != NULL) { - err = rate_spec->set(bebob, rate); + // NOTE: + // The firmware customized by M-Audio uses these commands to + // start transmitting stream. This is not usual way. + if (bebob->maudio_special_quirk) { + err = bebob->spec->rate->set(bebob, curr_rate); if (err < 0) { dev_err(&bebob->unit->device, "fail to ensure sampling rate: %d\n", err); - amdtp_stream_stop(&bebob->rx_stream); - break_both_connections(bebob); - goto end; + goto error; } } - /* wait first callback */ if (!amdtp_stream_wait_callback(&bebob->rx_stream, CALLBACK_TIMEOUT)) { - amdtp_stream_stop(&bebob->rx_stream); - break_both_connections(bebob); err = -ETIMEDOUT; - goto end; + goto error; } } - /* start slave if needed */ if (!amdtp_stream_running(&bebob->tx_stream)) { - err = start_stream(bebob, &bebob->tx_stream, rate); + err = start_stream(bebob, &bebob->tx_stream); if (err < 0) { dev_err(&bebob->unit->device, "fail to run AMDTP slave stream:%d\n", err); - amdtp_stream_stop(&bebob->rx_stream); - break_both_connections(bebob); - goto end; + goto error; } - /* wait first callback */ if (!amdtp_stream_wait_callback(&bebob->tx_stream, CALLBACK_TIMEOUT)) { - amdtp_stream_stop(&bebob->tx_stream); - amdtp_stream_stop(&bebob->rx_stream); - break_both_connections(bebob); err = -ETIMEDOUT; + goto error; } } -end: + + return 0; +error: + amdtp_stream_stop(&bebob->tx_stream); + amdtp_stream_stop(&bebob->rx_stream); + break_both_connections(bebob); return err; } void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob) { if (bebob->substreams_counter == 0) { - amdtp_stream_pcm_abort(&bebob->rx_stream); amdtp_stream_stop(&bebob->rx_stream); - - amdtp_stream_pcm_abort(&bebob->tx_stream); amdtp_stream_stop(&bebob->tx_stream); break_both_connections(bebob); + + cmp_connection_release(&bebob->out_conn); + cmp_connection_release(&bebob->in_conn); } } @@ -702,10 +708,8 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob) */ void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob) { - amdtp_stream_destroy(&bebob->rx_stream); - amdtp_stream_destroy(&bebob->tx_stream); - - destroy_both_connections(bebob); + destroy_stream(bebob, &bebob->tx_stream); + destroy_stream(bebob, &bebob->rx_stream); } /* |