From 8ac6c7a54ed1b98d142dce24b11c6de6a1e239a5 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Tue, 22 Oct 2024 10:36:11 +0000 Subject: [PATCH] 修改4g拨号为QMI,需要在系统里后台执行quectel-CM --- kernel/sound/firewire/oxfw/oxfw-stream.c | 493 +++++++++++++++++++++++++++++++++--------------------- 1 files changed, 297 insertions(+), 196 deletions(-) diff --git a/kernel/sound/firewire/oxfw/oxfw-stream.c b/kernel/sound/firewire/oxfw/oxfw-stream.c index d9361f3..80c9dc1 100644 --- a/kernel/sound/firewire/oxfw/oxfw-stream.c +++ b/kernel/sound/firewire/oxfw/oxfw-stream.c @@ -1,9 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * oxfw_stream.c - a part of driver for OXFW970/971 based devices * * Copyright (c) 2014 Takashi Sakamoto - * - * Licensed under the terms of the GNU General Public License, version 2. */ #include "oxfw.h" @@ -101,85 +100,28 @@ 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); + err = cmp_connection_establish(conn); if (err < 0) - goto end; + return err; - err = cmp_connection_establish(conn, - amdtp_stream_get_max_payload(stream)); - if (err < 0) - goto end; - - err = amdtp_stream_start(stream, - conn->resources.channel, - conn->speed); + err = amdtp_domain_add_stream(&oxfw->domain, stream, + conn->resources.channel, conn->speed); if (err < 0) { cmp_connection_break(conn); - goto end; + return err; } - /* Wait first packet */ - if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) { - stop_stream(oxfw, stream); - err = -ETIMEDOUT; - } -end: - return err; + return 0; } static int check_connection_used_by_others(struct snd_oxfw *oxfw, @@ -206,8 +148,7 @@ 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; @@ -226,13 +167,12 @@ 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; } /* @@ -246,115 +186,222 @@ 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; - } else { - substreams = oxfw->playback_substreams; - opposite_substreams = oxfw->capture_substreams; - - if (oxfw->has_output) - opposite = &oxfw->rx_stream; - else - opposite = NULL; - + if (stream == &oxfw->rx_stream) { dir = AVC_GENERAL_PLUG_DIR_IN; + formats = oxfw->rx_stream_formats; + conn = &oxfw->in_conn; + } else { + dir = AVC_GENERAL_PLUG_DIR_OUT; + formats = oxfw->tx_stream_formats; + conn = &oxfw->out_conn; } - - if (substreams == 0) - goto end; - - /* - * Considering JACK/FFADO streaming: - * TODO: This can be removed hwdep functionality becomes popular. - */ - err = check_connection_used_by_others(oxfw, stream); - if (err < 0) - goto end; - - /* packet queueing error */ - if (amdtp_streaming_error(stream)) - stop_stream(oxfw, stream); err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation); if (err < 0) - goto end; - if (rate == 0) + return err; + + 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; + + // The stream should have one pcm channels at least. + if (formation.pcm == 0) + return -EINVAL; + + err = amdtp_am824_set_parameters(stream, formation.rate, formation.pcm, + formation.midi * 8, false); + if (err < 0) + return err; + + 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, + unsigned int frames_per_period, + unsigned int frames_per_buffer) +{ + 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) + 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_domain_stop(&oxfw->domain); - 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); + cmp_connection_break(&oxfw->in_conn); + cmp_connection_release(&oxfw->in_conn); + + if (oxfw->has_output) { + 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) { + cmp_connection_release(&oxfw->in_conn); + return err; + } + } + + err = amdtp_domain_set_events_per_period(&oxfw->domain, + frames_per_period, frames_per_buffer); + if (err < 0) { + cmp_connection_release(&oxfw->in_conn); + if (oxfw->has_output) + cmp_connection_release(&oxfw->out_conn); + return err; + } + } + + 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_domain_stop(&oxfw->domain); + + cmp_connection_break(&oxfw->in_conn); + if (oxfw->has_output) + 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 prepare rx stream: %d\n", err); + goto error; + } + + if (oxfw->has_output && + !amdtp_stream_running(&oxfw->tx_stream)) { + err = start_stream(oxfw, &oxfw->tx_stream); if (err < 0) { dev_err(&oxfw->unit->device, - "fail to restart stream: %d\n", err); - goto end; + "fail to prepare tx stream: %d\n", err); + goto error; + } + } + + err = amdtp_domain_start(&oxfw->domain, 0); + if (err < 0) + goto error; + + // Wait first packet. + if (!amdtp_stream_wait_callback(&oxfw->rx_stream, + CALLBACK_TIMEOUT)) { + err = -ETIMEDOUT; + goto error; + } + + if (oxfw->has_output) { + if (!amdtp_stream_wait_callback(&oxfw->tx_stream, + CALLBACK_TIMEOUT)) { + err = -ETIMEDOUT; + goto error; } } } - /* Start requested stream. */ - if (!amdtp_stream_running(stream)) { - err = start_stream(oxfw, stream, rate, pcm_channels); - if (err < 0) - dev_err(&oxfw->unit->device, - "fail to start stream: %d\n", err); - } -end: + return 0; +error: + amdtp_domain_stop(&oxfw->domain); + + cmp_connection_break(&oxfw->in_conn); + if (oxfw->has_output) + cmp_connection_break(&oxfw->out_conn); + 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_domain_stop(&oxfw->domain); - stop_stream(oxfw, stream); + cmp_connection_break(&oxfw->in_conn); + cmp_connection_release(&oxfw->in_conn); + + if (oxfw->has_output) { + 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; @@ -367,20 +414,57 @@ 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; + } + } + + err = amdtp_domain_init(&oxfw->domain); + if (err < 0) { + destroy_stream(oxfw, &oxfw->rx_stream); + if (oxfw->has_output) + destroy_stream(oxfw, &oxfw->tx_stream); + } + + return err; +} + +// This function should be called before starting the stream or after stopping +// the streams. +void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw) +{ + amdtp_domain_destroy(&oxfw->domain); + + 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_domain_stop(&oxfw->domain); + + cmp_connection_break(&oxfw->in_conn); + + amdtp_stream_pcm_abort(&oxfw->rx_stream); + + if (oxfw->has_output) { + cmp_connection_break(&oxfw->out_conn); + + amdtp_stream_pcm_abort(&oxfw->tx_stream); + } } int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw, @@ -429,7 +513,7 @@ * Level 1: AM824 Compound (0x40) */ if ((format[0] != 0x90) || (format[1] != 0x40)) - return -ENOSYS; + return -ENXIO; /* check the sampling rate */ for (i = 0; i < ARRAY_SIZE(avc_stream_rate_table); i++) { @@ -437,7 +521,7 @@ break; } if (i == ARRAY_SIZE(avc_stream_rate_table)) - return -ENOSYS; + return -ENXIO; formation->rate = oxfw_rate_table[i]; @@ -481,13 +565,13 @@ /* Don't care */ case 0xff: default: - return -ENOSYS; /* not supported */ + return -ENXIO; /* not supported */ } } if (formation->pcm > AM824_MAX_CHANNELS_FOR_PCM || formation->midi > AM824_MAX_CHANNELS_FOR_MIDI) - return -ENOSYS; + return -ENXIO; return 0; } @@ -517,8 +601,9 @@ if (err < 0) goto end; - formats[eid] = kmemdup(buf, *len, GFP_KERNEL); - if (formats[eid] == NULL) { + formats[eid] = devm_kmemdup(&oxfw->card->card_dev, buf, *len, + GFP_KERNEL); + if (!formats[eid]) { err = -ENOMEM; goto end; } @@ -535,7 +620,8 @@ continue; eid++; - formats[eid] = kmemdup(buf, *len, GFP_KERNEL); + formats[eid] = devm_kmemdup(&oxfw->card->card_dev, buf, *len, + GFP_KERNEL); if (formats[eid] == NULL) { err = -ENOMEM; goto end; @@ -570,7 +656,7 @@ /* get first entry */ len = AVC_GENERIC_FRAME_MAXIMUM_BYTES; err = avc_stream_get_format_list(oxfw->unit, dir, 0, buf, &len, 0); - if (err == -ENOSYS) { + if (err == -ENXIO) { /* LIST subfunction is not implemented */ len = AVC_GENERIC_FRAME_MAXIMUM_BYTES; err = assume_stream_formats(oxfw, dir, pid, buf, &len, @@ -597,8 +683,9 @@ if (err < 0) break; - formats[eid] = kmemdup(buf, len, GFP_KERNEL); - if (formats[eid] == NULL) { + formats[eid] = devm_kmemdup(&oxfw->card->card_dev, buf, len, + GFP_KERNEL); + if (!formats[eid]) { err = -ENOMEM; break; } @@ -641,49 +728,63 @@ err); goto end; } else if ((plugs[0] == 0) && (plugs[1] == 0)) { - err = -ENOSYS; + err = -ENXIO; goto end; } /* use oPCR[0] if exists */ if (plugs[1] > 0) { err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_OUT, 0); - if (err < 0) - goto end; + if (err < 0) { + if (err != -ENXIO) + return err; - for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { - format = oxfw->tx_stream_formats[i]; - if (format == NULL) - continue; - err = snd_oxfw_stream_parse_format(format, &formation); - if (err < 0) - continue; + // The oPCR is not available for isoc communication. + err = 0; + } else { + for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { + format = oxfw->tx_stream_formats[i]; + if (format == NULL) + continue; + err = snd_oxfw_stream_parse_format(format, + &formation); + if (err < 0) + continue; - /* Add one MIDI port. */ - if (formation.midi > 0) - oxfw->midi_input_ports = 1; + /* Add one MIDI port. */ + if (formation.midi > 0) + oxfw->midi_input_ports = 1; + } + + oxfw->has_output = true; } - - oxfw->has_output = true; } /* use iPCR[0] if exists */ if (plugs[0] > 0) { err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_IN, 0); - if (err < 0) - goto end; + if (err < 0) { + if (err != -ENXIO) + return err; - for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { - format = oxfw->rx_stream_formats[i]; - if (format == NULL) - continue; - err = snd_oxfw_stream_parse_format(format, &formation); - if (err < 0) - continue; + // The iPCR is not available for isoc communication. + err = 0; + } else { + for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { + format = oxfw->rx_stream_formats[i]; + if (format == NULL) + continue; + err = snd_oxfw_stream_parse_format(format, + &formation); + if (err < 0) + continue; - /* Add one MIDI port. */ - if (formation.midi > 0) - oxfw->midi_output_ports = 1; + /* Add one MIDI port. */ + if (formation.midi > 0) + oxfw->midi_output_ports = 1; + } + + oxfw->has_input = true; } } end: -- Gitblit v1.6.2