| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * oxfw_stream.c - a part of driver for OXFW970/971 based devices |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (c) 2014 Takashi Sakamoto |
|---|
| 5 | | - * |
|---|
| 6 | | - * Licensed under the terms of the GNU General Public License, version 2. |
|---|
| 7 | 6 | */ |
|---|
| 8 | 7 | |
|---|
| 9 | 8 | #include "oxfw.h" |
|---|
| .. | .. |
|---|
| 101 | 100 | return 0; |
|---|
| 102 | 101 | } |
|---|
| 103 | 102 | |
|---|
| 104 | | -static void stop_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream) |
|---|
| 103 | +static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream) |
|---|
| 105 | 104 | { |
|---|
| 106 | | - amdtp_stream_pcm_abort(stream); |
|---|
| 107 | | - amdtp_stream_stop(stream); |
|---|
| 108 | | - |
|---|
| 109 | | - if (stream == &oxfw->tx_stream) |
|---|
| 110 | | - cmp_connection_break(&oxfw->out_conn); |
|---|
| 111 | | - else |
|---|
| 112 | | - cmp_connection_break(&oxfw->in_conn); |
|---|
| 113 | | -} |
|---|
| 114 | | - |
|---|
| 115 | | -static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream, |
|---|
| 116 | | - unsigned int rate, unsigned int pcm_channels) |
|---|
| 117 | | -{ |
|---|
| 118 | | - u8 **formats; |
|---|
| 119 | 105 | struct cmp_connection *conn; |
|---|
| 120 | | - struct snd_oxfw_stream_formation formation; |
|---|
| 121 | | - unsigned int i, midi_ports; |
|---|
| 122 | 106 | int err; |
|---|
| 123 | 107 | |
|---|
| 124 | | - if (stream == &oxfw->rx_stream) { |
|---|
| 125 | | - formats = oxfw->rx_stream_formats; |
|---|
| 108 | + if (stream == &oxfw->rx_stream) |
|---|
| 126 | 109 | conn = &oxfw->in_conn; |
|---|
| 127 | | - } else { |
|---|
| 128 | | - formats = oxfw->tx_stream_formats; |
|---|
| 110 | + else |
|---|
| 129 | 111 | conn = &oxfw->out_conn; |
|---|
| 130 | | - } |
|---|
| 131 | 112 | |
|---|
| 132 | | - /* Get stream format */ |
|---|
| 133 | | - for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { |
|---|
| 134 | | - if (formats[i] == NULL) |
|---|
| 135 | | - break; |
|---|
| 136 | | - |
|---|
| 137 | | - err = snd_oxfw_stream_parse_format(formats[i], &formation); |
|---|
| 138 | | - if (err < 0) |
|---|
| 139 | | - goto end; |
|---|
| 140 | | - if (rate != formation.rate) |
|---|
| 141 | | - continue; |
|---|
| 142 | | - if (pcm_channels == 0 || pcm_channels == formation.pcm) |
|---|
| 143 | | - break; |
|---|
| 144 | | - } |
|---|
| 145 | | - if (i == SND_OXFW_STREAM_FORMAT_ENTRIES) { |
|---|
| 146 | | - err = -EINVAL; |
|---|
| 147 | | - goto end; |
|---|
| 148 | | - } |
|---|
| 149 | | - |
|---|
| 150 | | - pcm_channels = formation.pcm; |
|---|
| 151 | | - midi_ports = formation.midi * 8; |
|---|
| 152 | | - |
|---|
| 153 | | - /* The stream should have one pcm channels at least */ |
|---|
| 154 | | - if (pcm_channels == 0) { |
|---|
| 155 | | - err = -EINVAL; |
|---|
| 156 | | - goto end; |
|---|
| 157 | | - } |
|---|
| 158 | | - err = amdtp_am824_set_parameters(stream, rate, pcm_channels, midi_ports, |
|---|
| 159 | | - false); |
|---|
| 113 | + err = cmp_connection_establish(conn); |
|---|
| 160 | 114 | if (err < 0) |
|---|
| 161 | | - goto end; |
|---|
| 115 | + return err; |
|---|
| 162 | 116 | |
|---|
| 163 | | - err = cmp_connection_establish(conn, |
|---|
| 164 | | - amdtp_stream_get_max_payload(stream)); |
|---|
| 165 | | - if (err < 0) |
|---|
| 166 | | - goto end; |
|---|
| 167 | | - |
|---|
| 168 | | - err = amdtp_stream_start(stream, |
|---|
| 169 | | - conn->resources.channel, |
|---|
| 170 | | - conn->speed); |
|---|
| 117 | + err = amdtp_domain_add_stream(&oxfw->domain, stream, |
|---|
| 118 | + conn->resources.channel, conn->speed); |
|---|
| 171 | 119 | if (err < 0) { |
|---|
| 172 | 120 | cmp_connection_break(conn); |
|---|
| 173 | | - goto end; |
|---|
| 121 | + return err; |
|---|
| 174 | 122 | } |
|---|
| 175 | 123 | |
|---|
| 176 | | - /* Wait first packet */ |
|---|
| 177 | | - if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) { |
|---|
| 178 | | - stop_stream(oxfw, stream); |
|---|
| 179 | | - err = -ETIMEDOUT; |
|---|
| 180 | | - } |
|---|
| 181 | | -end: |
|---|
| 182 | | - return err; |
|---|
| 124 | + return 0; |
|---|
| 183 | 125 | } |
|---|
| 184 | 126 | |
|---|
| 185 | 127 | static int check_connection_used_by_others(struct snd_oxfw *oxfw, |
|---|
| .. | .. |
|---|
| 206 | 148 | return err; |
|---|
| 207 | 149 | } |
|---|
| 208 | 150 | |
|---|
| 209 | | -int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw, |
|---|
| 210 | | - struct amdtp_stream *stream) |
|---|
| 151 | +static int init_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream) |
|---|
| 211 | 152 | { |
|---|
| 212 | 153 | struct cmp_connection *conn; |
|---|
| 213 | 154 | enum cmp_direction c_dir; |
|---|
| .. | .. |
|---|
| 226 | 167 | |
|---|
| 227 | 168 | err = cmp_connection_init(conn, oxfw->unit, c_dir, 0); |
|---|
| 228 | 169 | if (err < 0) |
|---|
| 229 | | - goto end; |
|---|
| 170 | + return err; |
|---|
| 230 | 171 | |
|---|
| 231 | 172 | err = amdtp_am824_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING); |
|---|
| 232 | 173 | if (err < 0) { |
|---|
| 233 | | - amdtp_stream_destroy(stream); |
|---|
| 234 | 174 | cmp_connection_destroy(conn); |
|---|
| 235 | | - goto end; |
|---|
| 175 | + return err; |
|---|
| 236 | 176 | } |
|---|
| 237 | 177 | |
|---|
| 238 | 178 | /* |
|---|
| .. | .. |
|---|
| 246 | 186 | if (oxfw->wrong_dbs) |
|---|
| 247 | 187 | oxfw->tx_stream.flags |= CIP_WRONG_DBS; |
|---|
| 248 | 188 | } |
|---|
| 249 | | -end: |
|---|
| 250 | | - return err; |
|---|
| 189 | + |
|---|
| 190 | + return 0; |
|---|
| 251 | 191 | } |
|---|
| 252 | 192 | |
|---|
| 253 | | -int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw, |
|---|
| 254 | | - struct amdtp_stream *stream, |
|---|
| 255 | | - unsigned int rate, unsigned int pcm_channels) |
|---|
| 193 | +static int keep_resources(struct snd_oxfw *oxfw, struct amdtp_stream *stream) |
|---|
| 256 | 194 | { |
|---|
| 257 | | - struct amdtp_stream *opposite; |
|---|
| 258 | | - struct snd_oxfw_stream_formation formation; |
|---|
| 259 | 195 | enum avc_general_plug_dir dir; |
|---|
| 260 | | - unsigned int substreams, opposite_substreams; |
|---|
| 261 | | - int err = 0; |
|---|
| 196 | + u8 **formats; |
|---|
| 197 | + struct snd_oxfw_stream_formation formation; |
|---|
| 198 | + struct cmp_connection *conn; |
|---|
| 199 | + int i; |
|---|
| 200 | + int err; |
|---|
| 262 | 201 | |
|---|
| 263 | | - if (stream == &oxfw->tx_stream) { |
|---|
| 264 | | - substreams = oxfw->capture_substreams; |
|---|
| 265 | | - opposite = &oxfw->rx_stream; |
|---|
| 266 | | - opposite_substreams = oxfw->playback_substreams; |
|---|
| 267 | | - dir = AVC_GENERAL_PLUG_DIR_OUT; |
|---|
| 268 | | - } else { |
|---|
| 269 | | - substreams = oxfw->playback_substreams; |
|---|
| 270 | | - opposite_substreams = oxfw->capture_substreams; |
|---|
| 271 | | - |
|---|
| 272 | | - if (oxfw->has_output) |
|---|
| 273 | | - opposite = &oxfw->rx_stream; |
|---|
| 274 | | - else |
|---|
| 275 | | - opposite = NULL; |
|---|
| 276 | | - |
|---|
| 202 | + if (stream == &oxfw->rx_stream) { |
|---|
| 277 | 203 | dir = AVC_GENERAL_PLUG_DIR_IN; |
|---|
| 204 | + formats = oxfw->rx_stream_formats; |
|---|
| 205 | + conn = &oxfw->in_conn; |
|---|
| 206 | + } else { |
|---|
| 207 | + dir = AVC_GENERAL_PLUG_DIR_OUT; |
|---|
| 208 | + formats = oxfw->tx_stream_formats; |
|---|
| 209 | + conn = &oxfw->out_conn; |
|---|
| 278 | 210 | } |
|---|
| 279 | | - |
|---|
| 280 | | - if (substreams == 0) |
|---|
| 281 | | - goto end; |
|---|
| 282 | | - |
|---|
| 283 | | - /* |
|---|
| 284 | | - * Considering JACK/FFADO streaming: |
|---|
| 285 | | - * TODO: This can be removed hwdep functionality becomes popular. |
|---|
| 286 | | - */ |
|---|
| 287 | | - err = check_connection_used_by_others(oxfw, stream); |
|---|
| 288 | | - if (err < 0) |
|---|
| 289 | | - goto end; |
|---|
| 290 | | - |
|---|
| 291 | | - /* packet queueing error */ |
|---|
| 292 | | - if (amdtp_streaming_error(stream)) |
|---|
| 293 | | - stop_stream(oxfw, stream); |
|---|
| 294 | 211 | |
|---|
| 295 | 212 | err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation); |
|---|
| 296 | 213 | if (err < 0) |
|---|
| 297 | | - goto end; |
|---|
| 298 | | - if (rate == 0) |
|---|
| 214 | + return err; |
|---|
| 215 | + |
|---|
| 216 | + for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { |
|---|
| 217 | + struct snd_oxfw_stream_formation fmt; |
|---|
| 218 | + |
|---|
| 219 | + if (formats[i] == NULL) |
|---|
| 220 | + break; |
|---|
| 221 | + |
|---|
| 222 | + err = snd_oxfw_stream_parse_format(formats[i], &fmt); |
|---|
| 223 | + if (err < 0) |
|---|
| 224 | + return err; |
|---|
| 225 | + |
|---|
| 226 | + if (fmt.rate == formation.rate && fmt.pcm == formation.pcm && |
|---|
| 227 | + fmt.midi == formation.midi) |
|---|
| 228 | + break; |
|---|
| 229 | + } |
|---|
| 230 | + if (i == SND_OXFW_STREAM_FORMAT_ENTRIES) |
|---|
| 231 | + return -EINVAL; |
|---|
| 232 | + |
|---|
| 233 | + // The stream should have one pcm channels at least. |
|---|
| 234 | + if (formation.pcm == 0) |
|---|
| 235 | + return -EINVAL; |
|---|
| 236 | + |
|---|
| 237 | + err = amdtp_am824_set_parameters(stream, formation.rate, formation.pcm, |
|---|
| 238 | + formation.midi * 8, false); |
|---|
| 239 | + if (err < 0) |
|---|
| 240 | + return err; |
|---|
| 241 | + |
|---|
| 242 | + return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream)); |
|---|
| 243 | +} |
|---|
| 244 | + |
|---|
| 245 | +int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw, |
|---|
| 246 | + struct amdtp_stream *stream, |
|---|
| 247 | + unsigned int rate, unsigned int pcm_channels, |
|---|
| 248 | + unsigned int frames_per_period, |
|---|
| 249 | + unsigned int frames_per_buffer) |
|---|
| 250 | +{ |
|---|
| 251 | + struct snd_oxfw_stream_formation formation; |
|---|
| 252 | + enum avc_general_plug_dir dir; |
|---|
| 253 | + int err; |
|---|
| 254 | + |
|---|
| 255 | + // Considering JACK/FFADO streaming: |
|---|
| 256 | + // TODO: This can be removed hwdep functionality becomes popular. |
|---|
| 257 | + err = check_connection_used_by_others(oxfw, &oxfw->rx_stream); |
|---|
| 258 | + if (err < 0) |
|---|
| 259 | + return err; |
|---|
| 260 | + if (oxfw->has_output) { |
|---|
| 261 | + err = check_connection_used_by_others(oxfw, &oxfw->tx_stream); |
|---|
| 262 | + if (err < 0) |
|---|
| 263 | + return err; |
|---|
| 264 | + } |
|---|
| 265 | + |
|---|
| 266 | + if (stream == &oxfw->tx_stream) |
|---|
| 267 | + dir = AVC_GENERAL_PLUG_DIR_OUT; |
|---|
| 268 | + else |
|---|
| 269 | + dir = AVC_GENERAL_PLUG_DIR_IN; |
|---|
| 270 | + |
|---|
| 271 | + err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation); |
|---|
| 272 | + if (err < 0) |
|---|
| 273 | + return err; |
|---|
| 274 | + if (rate == 0) { |
|---|
| 299 | 275 | rate = formation.rate; |
|---|
| 300 | | - if (pcm_channels == 0) |
|---|
| 301 | 276 | pcm_channels = formation.pcm; |
|---|
| 277 | + } |
|---|
| 278 | + if (formation.rate != rate || formation.pcm != pcm_channels) { |
|---|
| 279 | + amdtp_domain_stop(&oxfw->domain); |
|---|
| 302 | 280 | |
|---|
| 303 | | - if ((formation.rate != rate) || (formation.pcm != pcm_channels)) { |
|---|
| 304 | | - if (opposite != NULL) { |
|---|
| 305 | | - err = check_connection_used_by_others(oxfw, opposite); |
|---|
| 306 | | - if (err < 0) |
|---|
| 307 | | - goto end; |
|---|
| 308 | | - stop_stream(oxfw, opposite); |
|---|
| 281 | + cmp_connection_break(&oxfw->in_conn); |
|---|
| 282 | + cmp_connection_release(&oxfw->in_conn); |
|---|
| 283 | + |
|---|
| 284 | + if (oxfw->has_output) { |
|---|
| 285 | + cmp_connection_break(&oxfw->out_conn); |
|---|
| 286 | + cmp_connection_release(&oxfw->out_conn); |
|---|
| 309 | 287 | } |
|---|
| 310 | | - stop_stream(oxfw, stream); |
|---|
| 288 | + } |
|---|
| 311 | 289 | |
|---|
| 290 | + if (oxfw->substreams_count == 0 || |
|---|
| 291 | + formation.rate != rate || formation.pcm != pcm_channels) { |
|---|
| 312 | 292 | err = set_stream_format(oxfw, stream, rate, pcm_channels); |
|---|
| 313 | 293 | if (err < 0) { |
|---|
| 314 | 294 | dev_err(&oxfw->unit->device, |
|---|
| 315 | 295 | "fail to set stream format: %d\n", err); |
|---|
| 316 | | - goto end; |
|---|
| 296 | + return err; |
|---|
| 317 | 297 | } |
|---|
| 318 | 298 | |
|---|
| 319 | | - /* Start opposite stream if needed. */ |
|---|
| 320 | | - if (opposite && !amdtp_stream_running(opposite) && |
|---|
| 321 | | - (opposite_substreams > 0)) { |
|---|
| 322 | | - err = start_stream(oxfw, opposite, rate, 0); |
|---|
| 299 | + err = keep_resources(oxfw, &oxfw->rx_stream); |
|---|
| 300 | + if (err < 0) |
|---|
| 301 | + return err; |
|---|
| 302 | + |
|---|
| 303 | + if (oxfw->has_output) { |
|---|
| 304 | + err = keep_resources(oxfw, &oxfw->tx_stream); |
|---|
| 305 | + if (err < 0) { |
|---|
| 306 | + cmp_connection_release(&oxfw->in_conn); |
|---|
| 307 | + return err; |
|---|
| 308 | + } |
|---|
| 309 | + } |
|---|
| 310 | + |
|---|
| 311 | + err = amdtp_domain_set_events_per_period(&oxfw->domain, |
|---|
| 312 | + frames_per_period, frames_per_buffer); |
|---|
| 313 | + if (err < 0) { |
|---|
| 314 | + cmp_connection_release(&oxfw->in_conn); |
|---|
| 315 | + if (oxfw->has_output) |
|---|
| 316 | + cmp_connection_release(&oxfw->out_conn); |
|---|
| 317 | + return err; |
|---|
| 318 | + } |
|---|
| 319 | + } |
|---|
| 320 | + |
|---|
| 321 | + return 0; |
|---|
| 322 | +} |
|---|
| 323 | + |
|---|
| 324 | +int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw) |
|---|
| 325 | +{ |
|---|
| 326 | + int err; |
|---|
| 327 | + |
|---|
| 328 | + if (oxfw->substreams_count == 0) |
|---|
| 329 | + return -EIO; |
|---|
| 330 | + |
|---|
| 331 | + if (amdtp_streaming_error(&oxfw->rx_stream) || |
|---|
| 332 | + amdtp_streaming_error(&oxfw->tx_stream)) { |
|---|
| 333 | + amdtp_domain_stop(&oxfw->domain); |
|---|
| 334 | + |
|---|
| 335 | + cmp_connection_break(&oxfw->in_conn); |
|---|
| 336 | + if (oxfw->has_output) |
|---|
| 337 | + cmp_connection_break(&oxfw->out_conn); |
|---|
| 338 | + } |
|---|
| 339 | + |
|---|
| 340 | + if (!amdtp_stream_running(&oxfw->rx_stream)) { |
|---|
| 341 | + err = start_stream(oxfw, &oxfw->rx_stream); |
|---|
| 342 | + if (err < 0) { |
|---|
| 343 | + dev_err(&oxfw->unit->device, |
|---|
| 344 | + "fail to prepare rx stream: %d\n", err); |
|---|
| 345 | + goto error; |
|---|
| 346 | + } |
|---|
| 347 | + |
|---|
| 348 | + if (oxfw->has_output && |
|---|
| 349 | + !amdtp_stream_running(&oxfw->tx_stream)) { |
|---|
| 350 | + err = start_stream(oxfw, &oxfw->tx_stream); |
|---|
| 323 | 351 | if (err < 0) { |
|---|
| 324 | 352 | dev_err(&oxfw->unit->device, |
|---|
| 325 | | - "fail to restart stream: %d\n", err); |
|---|
| 326 | | - goto end; |
|---|
| 353 | + "fail to prepare tx stream: %d\n", err); |
|---|
| 354 | + goto error; |
|---|
| 355 | + } |
|---|
| 356 | + } |
|---|
| 357 | + |
|---|
| 358 | + err = amdtp_domain_start(&oxfw->domain, 0); |
|---|
| 359 | + if (err < 0) |
|---|
| 360 | + goto error; |
|---|
| 361 | + |
|---|
| 362 | + // Wait first packet. |
|---|
| 363 | + if (!amdtp_stream_wait_callback(&oxfw->rx_stream, |
|---|
| 364 | + CALLBACK_TIMEOUT)) { |
|---|
| 365 | + err = -ETIMEDOUT; |
|---|
| 366 | + goto error; |
|---|
| 367 | + } |
|---|
| 368 | + |
|---|
| 369 | + if (oxfw->has_output) { |
|---|
| 370 | + if (!amdtp_stream_wait_callback(&oxfw->tx_stream, |
|---|
| 371 | + CALLBACK_TIMEOUT)) { |
|---|
| 372 | + err = -ETIMEDOUT; |
|---|
| 373 | + goto error; |
|---|
| 327 | 374 | } |
|---|
| 328 | 375 | } |
|---|
| 329 | 376 | } |
|---|
| 330 | 377 | |
|---|
| 331 | | - /* Start requested stream. */ |
|---|
| 332 | | - if (!amdtp_stream_running(stream)) { |
|---|
| 333 | | - err = start_stream(oxfw, stream, rate, pcm_channels); |
|---|
| 334 | | - if (err < 0) |
|---|
| 335 | | - dev_err(&oxfw->unit->device, |
|---|
| 336 | | - "fail to start stream: %d\n", err); |
|---|
| 337 | | - } |
|---|
| 338 | | -end: |
|---|
| 378 | + return 0; |
|---|
| 379 | +error: |
|---|
| 380 | + amdtp_domain_stop(&oxfw->domain); |
|---|
| 381 | + |
|---|
| 382 | + cmp_connection_break(&oxfw->in_conn); |
|---|
| 383 | + if (oxfw->has_output) |
|---|
| 384 | + cmp_connection_break(&oxfw->out_conn); |
|---|
| 385 | + |
|---|
| 339 | 386 | return err; |
|---|
| 340 | 387 | } |
|---|
| 341 | 388 | |
|---|
| 342 | | -void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw, |
|---|
| 343 | | - struct amdtp_stream *stream) |
|---|
| 389 | +void snd_oxfw_stream_stop_duplex(struct snd_oxfw *oxfw) |
|---|
| 344 | 390 | { |
|---|
| 345 | | - if (((stream == &oxfw->tx_stream) && (oxfw->capture_substreams > 0)) || |
|---|
| 346 | | - ((stream == &oxfw->rx_stream) && (oxfw->playback_substreams > 0))) |
|---|
| 347 | | - return; |
|---|
| 391 | + if (oxfw->substreams_count == 0) { |
|---|
| 392 | + amdtp_domain_stop(&oxfw->domain); |
|---|
| 348 | 393 | |
|---|
| 349 | | - stop_stream(oxfw, stream); |
|---|
| 394 | + cmp_connection_break(&oxfw->in_conn); |
|---|
| 395 | + cmp_connection_release(&oxfw->in_conn); |
|---|
| 396 | + |
|---|
| 397 | + if (oxfw->has_output) { |
|---|
| 398 | + cmp_connection_break(&oxfw->out_conn); |
|---|
| 399 | + cmp_connection_release(&oxfw->out_conn); |
|---|
| 400 | + } |
|---|
| 401 | + } |
|---|
| 350 | 402 | } |
|---|
| 351 | 403 | |
|---|
| 352 | | -/* |
|---|
| 353 | | - * This function should be called before starting the stream or after stopping |
|---|
| 354 | | - * the streams. |
|---|
| 355 | | - */ |
|---|
| 356 | | -void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw, |
|---|
| 357 | | - struct amdtp_stream *stream) |
|---|
| 404 | +static void destroy_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream) |
|---|
| 358 | 405 | { |
|---|
| 359 | 406 | struct cmp_connection *conn; |
|---|
| 360 | 407 | |
|---|
| .. | .. |
|---|
| 367 | 414 | cmp_connection_destroy(conn); |
|---|
| 368 | 415 | } |
|---|
| 369 | 416 | |
|---|
| 370 | | -void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw, |
|---|
| 371 | | - struct amdtp_stream *stream) |
|---|
| 417 | +int snd_oxfw_stream_init_duplex(struct snd_oxfw *oxfw) |
|---|
| 372 | 418 | { |
|---|
| 373 | | - struct cmp_connection *conn; |
|---|
| 419 | + int err; |
|---|
| 374 | 420 | |
|---|
| 375 | | - if (stream == &oxfw->tx_stream) |
|---|
| 376 | | - conn = &oxfw->out_conn; |
|---|
| 377 | | - else |
|---|
| 378 | | - conn = &oxfw->in_conn; |
|---|
| 421 | + err = init_stream(oxfw, &oxfw->rx_stream); |
|---|
| 422 | + if (err < 0) |
|---|
| 423 | + return err; |
|---|
| 379 | 424 | |
|---|
| 380 | | - if (cmp_connection_update(conn) < 0) |
|---|
| 381 | | - stop_stream(oxfw, stream); |
|---|
| 382 | | - else |
|---|
| 383 | | - amdtp_stream_update(stream); |
|---|
| 425 | + if (oxfw->has_output) { |
|---|
| 426 | + err = init_stream(oxfw, &oxfw->tx_stream); |
|---|
| 427 | + if (err < 0) { |
|---|
| 428 | + destroy_stream(oxfw, &oxfw->rx_stream); |
|---|
| 429 | + return err; |
|---|
| 430 | + } |
|---|
| 431 | + } |
|---|
| 432 | + |
|---|
| 433 | + err = amdtp_domain_init(&oxfw->domain); |
|---|
| 434 | + if (err < 0) { |
|---|
| 435 | + destroy_stream(oxfw, &oxfw->rx_stream); |
|---|
| 436 | + if (oxfw->has_output) |
|---|
| 437 | + destroy_stream(oxfw, &oxfw->tx_stream); |
|---|
| 438 | + } |
|---|
| 439 | + |
|---|
| 440 | + return err; |
|---|
| 441 | +} |
|---|
| 442 | + |
|---|
| 443 | +// This function should be called before starting the stream or after stopping |
|---|
| 444 | +// the streams. |
|---|
| 445 | +void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw) |
|---|
| 446 | +{ |
|---|
| 447 | + amdtp_domain_destroy(&oxfw->domain); |
|---|
| 448 | + |
|---|
| 449 | + destroy_stream(oxfw, &oxfw->rx_stream); |
|---|
| 450 | + |
|---|
| 451 | + if (oxfw->has_output) |
|---|
| 452 | + destroy_stream(oxfw, &oxfw->tx_stream); |
|---|
| 453 | +} |
|---|
| 454 | + |
|---|
| 455 | +void snd_oxfw_stream_update_duplex(struct snd_oxfw *oxfw) |
|---|
| 456 | +{ |
|---|
| 457 | + amdtp_domain_stop(&oxfw->domain); |
|---|
| 458 | + |
|---|
| 459 | + cmp_connection_break(&oxfw->in_conn); |
|---|
| 460 | + |
|---|
| 461 | + amdtp_stream_pcm_abort(&oxfw->rx_stream); |
|---|
| 462 | + |
|---|
| 463 | + if (oxfw->has_output) { |
|---|
| 464 | + cmp_connection_break(&oxfw->out_conn); |
|---|
| 465 | + |
|---|
| 466 | + amdtp_stream_pcm_abort(&oxfw->tx_stream); |
|---|
| 467 | + } |
|---|
| 384 | 468 | } |
|---|
| 385 | 469 | |
|---|
| 386 | 470 | int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw, |
|---|
| .. | .. |
|---|
| 429 | 513 | * Level 1: AM824 Compound (0x40) |
|---|
| 430 | 514 | */ |
|---|
| 431 | 515 | if ((format[0] != 0x90) || (format[1] != 0x40)) |
|---|
| 432 | | - return -ENOSYS; |
|---|
| 516 | + return -ENXIO; |
|---|
| 433 | 517 | |
|---|
| 434 | 518 | /* check the sampling rate */ |
|---|
| 435 | 519 | for (i = 0; i < ARRAY_SIZE(avc_stream_rate_table); i++) { |
|---|
| .. | .. |
|---|
| 437 | 521 | break; |
|---|
| 438 | 522 | } |
|---|
| 439 | 523 | if (i == ARRAY_SIZE(avc_stream_rate_table)) |
|---|
| 440 | | - return -ENOSYS; |
|---|
| 524 | + return -ENXIO; |
|---|
| 441 | 525 | |
|---|
| 442 | 526 | formation->rate = oxfw_rate_table[i]; |
|---|
| 443 | 527 | |
|---|
| .. | .. |
|---|
| 481 | 565 | /* Don't care */ |
|---|
| 482 | 566 | case 0xff: |
|---|
| 483 | 567 | default: |
|---|
| 484 | | - return -ENOSYS; /* not supported */ |
|---|
| 568 | + return -ENXIO; /* not supported */ |
|---|
| 485 | 569 | } |
|---|
| 486 | 570 | } |
|---|
| 487 | 571 | |
|---|
| 488 | 572 | if (formation->pcm > AM824_MAX_CHANNELS_FOR_PCM || |
|---|
| 489 | 573 | formation->midi > AM824_MAX_CHANNELS_FOR_MIDI) |
|---|
| 490 | | - return -ENOSYS; |
|---|
| 574 | + return -ENXIO; |
|---|
| 491 | 575 | |
|---|
| 492 | 576 | return 0; |
|---|
| 493 | 577 | } |
|---|
| .. | .. |
|---|
| 517 | 601 | if (err < 0) |
|---|
| 518 | 602 | goto end; |
|---|
| 519 | 603 | |
|---|
| 520 | | - formats[eid] = kmemdup(buf, *len, GFP_KERNEL); |
|---|
| 521 | | - if (formats[eid] == NULL) { |
|---|
| 604 | + formats[eid] = devm_kmemdup(&oxfw->card->card_dev, buf, *len, |
|---|
| 605 | + GFP_KERNEL); |
|---|
| 606 | + if (!formats[eid]) { |
|---|
| 522 | 607 | err = -ENOMEM; |
|---|
| 523 | 608 | goto end; |
|---|
| 524 | 609 | } |
|---|
| .. | .. |
|---|
| 535 | 620 | continue; |
|---|
| 536 | 621 | |
|---|
| 537 | 622 | eid++; |
|---|
| 538 | | - formats[eid] = kmemdup(buf, *len, GFP_KERNEL); |
|---|
| 623 | + formats[eid] = devm_kmemdup(&oxfw->card->card_dev, buf, *len, |
|---|
| 624 | + GFP_KERNEL); |
|---|
| 539 | 625 | if (formats[eid] == NULL) { |
|---|
| 540 | 626 | err = -ENOMEM; |
|---|
| 541 | 627 | goto end; |
|---|
| .. | .. |
|---|
| 570 | 656 | /* get first entry */ |
|---|
| 571 | 657 | len = AVC_GENERIC_FRAME_MAXIMUM_BYTES; |
|---|
| 572 | 658 | err = avc_stream_get_format_list(oxfw->unit, dir, 0, buf, &len, 0); |
|---|
| 573 | | - if (err == -ENOSYS) { |
|---|
| 659 | + if (err == -ENXIO) { |
|---|
| 574 | 660 | /* LIST subfunction is not implemented */ |
|---|
| 575 | 661 | len = AVC_GENERIC_FRAME_MAXIMUM_BYTES; |
|---|
| 576 | 662 | err = assume_stream_formats(oxfw, dir, pid, buf, &len, |
|---|
| .. | .. |
|---|
| 597 | 683 | if (err < 0) |
|---|
| 598 | 684 | break; |
|---|
| 599 | 685 | |
|---|
| 600 | | - formats[eid] = kmemdup(buf, len, GFP_KERNEL); |
|---|
| 601 | | - if (formats[eid] == NULL) { |
|---|
| 686 | + formats[eid] = devm_kmemdup(&oxfw->card->card_dev, buf, len, |
|---|
| 687 | + GFP_KERNEL); |
|---|
| 688 | + if (!formats[eid]) { |
|---|
| 602 | 689 | err = -ENOMEM; |
|---|
| 603 | 690 | break; |
|---|
| 604 | 691 | } |
|---|
| .. | .. |
|---|
| 641 | 728 | err); |
|---|
| 642 | 729 | goto end; |
|---|
| 643 | 730 | } else if ((plugs[0] == 0) && (plugs[1] == 0)) { |
|---|
| 644 | | - err = -ENOSYS; |
|---|
| 731 | + err = -ENXIO; |
|---|
| 645 | 732 | goto end; |
|---|
| 646 | 733 | } |
|---|
| 647 | 734 | |
|---|
| 648 | 735 | /* use oPCR[0] if exists */ |
|---|
| 649 | 736 | if (plugs[1] > 0) { |
|---|
| 650 | 737 | err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_OUT, 0); |
|---|
| 651 | | - if (err < 0) |
|---|
| 652 | | - goto end; |
|---|
| 738 | + if (err < 0) { |
|---|
| 739 | + if (err != -ENXIO) |
|---|
| 740 | + return err; |
|---|
| 653 | 741 | |
|---|
| 654 | | - for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { |
|---|
| 655 | | - format = oxfw->tx_stream_formats[i]; |
|---|
| 656 | | - if (format == NULL) |
|---|
| 657 | | - continue; |
|---|
| 658 | | - err = snd_oxfw_stream_parse_format(format, &formation); |
|---|
| 659 | | - if (err < 0) |
|---|
| 660 | | - continue; |
|---|
| 742 | + // The oPCR is not available for isoc communication. |
|---|
| 743 | + err = 0; |
|---|
| 744 | + } else { |
|---|
| 745 | + for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { |
|---|
| 746 | + format = oxfw->tx_stream_formats[i]; |
|---|
| 747 | + if (format == NULL) |
|---|
| 748 | + continue; |
|---|
| 749 | + err = snd_oxfw_stream_parse_format(format, |
|---|
| 750 | + &formation); |
|---|
| 751 | + if (err < 0) |
|---|
| 752 | + continue; |
|---|
| 661 | 753 | |
|---|
| 662 | | - /* Add one MIDI port. */ |
|---|
| 663 | | - if (formation.midi > 0) |
|---|
| 664 | | - oxfw->midi_input_ports = 1; |
|---|
| 754 | + /* Add one MIDI port. */ |
|---|
| 755 | + if (formation.midi > 0) |
|---|
| 756 | + oxfw->midi_input_ports = 1; |
|---|
| 757 | + } |
|---|
| 758 | + |
|---|
| 759 | + oxfw->has_output = true; |
|---|
| 665 | 760 | } |
|---|
| 666 | | - |
|---|
| 667 | | - oxfw->has_output = true; |
|---|
| 668 | 761 | } |
|---|
| 669 | 762 | |
|---|
| 670 | 763 | /* use iPCR[0] if exists */ |
|---|
| 671 | 764 | if (plugs[0] > 0) { |
|---|
| 672 | 765 | err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_IN, 0); |
|---|
| 673 | | - if (err < 0) |
|---|
| 674 | | - goto end; |
|---|
| 766 | + if (err < 0) { |
|---|
| 767 | + if (err != -ENXIO) |
|---|
| 768 | + return err; |
|---|
| 675 | 769 | |
|---|
| 676 | | - for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { |
|---|
| 677 | | - format = oxfw->rx_stream_formats[i]; |
|---|
| 678 | | - if (format == NULL) |
|---|
| 679 | | - continue; |
|---|
| 680 | | - err = snd_oxfw_stream_parse_format(format, &formation); |
|---|
| 681 | | - if (err < 0) |
|---|
| 682 | | - continue; |
|---|
| 770 | + // The iPCR is not available for isoc communication. |
|---|
| 771 | + err = 0; |
|---|
| 772 | + } else { |
|---|
| 773 | + for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { |
|---|
| 774 | + format = oxfw->rx_stream_formats[i]; |
|---|
| 775 | + if (format == NULL) |
|---|
| 776 | + continue; |
|---|
| 777 | + err = snd_oxfw_stream_parse_format(format, |
|---|
| 778 | + &formation); |
|---|
| 779 | + if (err < 0) |
|---|
| 780 | + continue; |
|---|
| 683 | 781 | |
|---|
| 684 | | - /* Add one MIDI port. */ |
|---|
| 685 | | - if (formation.midi > 0) |
|---|
| 686 | | - oxfw->midi_output_ports = 1; |
|---|
| 782 | + /* Add one MIDI port. */ |
|---|
| 783 | + if (formation.midi > 0) |
|---|
| 784 | + oxfw->midi_output_ports = 1; |
|---|
| 785 | + } |
|---|
| 786 | + |
|---|
| 787 | + oxfw->has_input = true; |
|---|
| 687 | 788 | } |
|---|
| 688 | 789 | } |
|---|
| 689 | 790 | end: |
|---|