| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Digital Audio (PCM) abstract layer |
|---|
| 3 | 4 | * Copyright (c) by Jaroslav Kysela <perex@perex.cz> |
|---|
| 4 | 5 | * Abramo Bagnara <abramo@alsa-project.org> |
|---|
| 5 | | - * |
|---|
| 6 | | - * |
|---|
| 7 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 8 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 9 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 10 | | - * (at your option) any later version. |
|---|
| 11 | | - * |
|---|
| 12 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 13 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 14 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 15 | | - * GNU General Public License for more details. |
|---|
| 16 | | - * |
|---|
| 17 | | - * You should have received a copy of the GNU General Public License |
|---|
| 18 | | - * along with this program; if not, write to the Free Software |
|---|
| 19 | | - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|---|
| 20 | | - * |
|---|
| 21 | 6 | */ |
|---|
| 22 | 7 | |
|---|
| 23 | 8 | #include <linux/slab.h> |
|---|
| .. | .. |
|---|
| 45 | 30 | #define trace_applptr(substream, prev, curr) |
|---|
| 46 | 31 | #define trace_applptr_start(substream, frame) |
|---|
| 47 | 32 | #endif |
|---|
| 48 | | - |
|---|
| 49 | | -#define STRING_LENGTH_OF_INT 12 |
|---|
| 50 | | -#define MAX_USR_CTRL_CNT 128 |
|---|
| 51 | 33 | |
|---|
| 52 | 34 | static int fill_silence_frames(struct snd_pcm_substream *substream, |
|---|
| 53 | 35 | snd_pcm_uframes_t off, snd_pcm_uframes_t frames); |
|---|
| .. | .. |
|---|
| 163 | 145 | struct snd_pcm_runtime *runtime = substream->runtime; |
|---|
| 164 | 146 | |
|---|
| 165 | 147 | trace_xrun(substream); |
|---|
| 166 | | - if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) |
|---|
| 167 | | - snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); |
|---|
| 148 | + if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { |
|---|
| 149 | + struct timespec64 tstamp; |
|---|
| 150 | + |
|---|
| 151 | + snd_pcm_gettime(runtime, &tstamp); |
|---|
| 152 | + runtime->status->tstamp.tv_sec = tstamp.tv_sec; |
|---|
| 153 | + runtime->status->tstamp.tv_nsec = tstamp.tv_nsec; |
|---|
| 154 | + } |
|---|
| 168 | 155 | snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); |
|---|
| 169 | 156 | if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { |
|---|
| 170 | 157 | char name[16]; |
|---|
| .. | .. |
|---|
| 219 | 206 | } |
|---|
| 220 | 207 | |
|---|
| 221 | 208 | static void update_audio_tstamp(struct snd_pcm_substream *substream, |
|---|
| 222 | | - struct timespec *curr_tstamp, |
|---|
| 223 | | - struct timespec *audio_tstamp) |
|---|
| 209 | + struct timespec64 *curr_tstamp, |
|---|
| 210 | + struct timespec64 *audio_tstamp) |
|---|
| 224 | 211 | { |
|---|
| 225 | 212 | struct snd_pcm_runtime *runtime = substream->runtime; |
|---|
| 226 | 213 | u64 audio_frames, audio_nsecs; |
|---|
| 227 | | - struct timespec driver_tstamp; |
|---|
| 214 | + struct timespec64 driver_tstamp; |
|---|
| 228 | 215 | |
|---|
| 229 | 216 | if (runtime->tstamp_mode != SNDRV_PCM_TSTAMP_ENABLE) |
|---|
| 230 | 217 | return; |
|---|
| .. | .. |
|---|
| 248 | 235 | } |
|---|
| 249 | 236 | audio_nsecs = div_u64(audio_frames * 1000000000LL, |
|---|
| 250 | 237 | runtime->rate); |
|---|
| 251 | | - *audio_tstamp = ns_to_timespec(audio_nsecs); |
|---|
| 238 | + *audio_tstamp = ns_to_timespec64(audio_nsecs); |
|---|
| 252 | 239 | } |
|---|
| 253 | | - if (!timespec_equal(&runtime->status->audio_tstamp, audio_tstamp)) { |
|---|
| 254 | | - runtime->status->audio_tstamp = *audio_tstamp; |
|---|
| 255 | | - runtime->status->tstamp = *curr_tstamp; |
|---|
| 240 | + |
|---|
| 241 | + if (runtime->status->audio_tstamp.tv_sec != audio_tstamp->tv_sec || |
|---|
| 242 | + runtime->status->audio_tstamp.tv_nsec != audio_tstamp->tv_nsec) { |
|---|
| 243 | + runtime->status->audio_tstamp.tv_sec = audio_tstamp->tv_sec; |
|---|
| 244 | + runtime->status->audio_tstamp.tv_nsec = audio_tstamp->tv_nsec; |
|---|
| 245 | + runtime->status->tstamp.tv_sec = curr_tstamp->tv_sec; |
|---|
| 246 | + runtime->status->tstamp.tv_nsec = curr_tstamp->tv_nsec; |
|---|
| 256 | 247 | } |
|---|
| 248 | + |
|---|
| 257 | 249 | |
|---|
| 258 | 250 | /* |
|---|
| 259 | 251 | * re-take a driver timestamp to let apps detect if the reference tstamp |
|---|
| 260 | 252 | * read by low-level hardware was provided with a delay |
|---|
| 261 | 253 | */ |
|---|
| 262 | | - snd_pcm_gettime(substream->runtime, (struct timespec *)&driver_tstamp); |
|---|
| 254 | + snd_pcm_gettime(substream->runtime, &driver_tstamp); |
|---|
| 263 | 255 | runtime->driver_tstamp = driver_tstamp; |
|---|
| 264 | 256 | } |
|---|
| 265 | 257 | |
|---|
| .. | .. |
|---|
| 272 | 264 | snd_pcm_sframes_t hdelta, delta; |
|---|
| 273 | 265 | unsigned long jdelta; |
|---|
| 274 | 266 | unsigned long curr_jiffies; |
|---|
| 275 | | - struct timespec curr_tstamp; |
|---|
| 276 | | - struct timespec audio_tstamp; |
|---|
| 267 | + struct timespec64 curr_tstamp; |
|---|
| 268 | + struct timespec64 audio_tstamp; |
|---|
| 277 | 269 | int crossed_boundary = 0; |
|---|
| 278 | 270 | |
|---|
| 279 | 271 | old_hw_ptr = runtime->status->hw_ptr; |
|---|
| .. | .. |
|---|
| 296 | 288 | |
|---|
| 297 | 289 | /* re-test in case tstamp type is not supported in hardware and was demoted to DEFAULT */ |
|---|
| 298 | 290 | if (runtime->audio_tstamp_report.actual_type == SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT) |
|---|
| 299 | | - snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); |
|---|
| 291 | + snd_pcm_gettime(runtime, &curr_tstamp); |
|---|
| 300 | 292 | } else |
|---|
| 301 | | - snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); |
|---|
| 293 | + snd_pcm_gettime(runtime, &curr_tstamp); |
|---|
| 302 | 294 | } |
|---|
| 303 | 295 | |
|---|
| 304 | 296 | if (pos == SNDRV_PCM_POS_XRUN) { |
|---|
| .. | .. |
|---|
| 499 | 491 | EXPORT_SYMBOL(snd_pcm_set_ops); |
|---|
| 500 | 492 | |
|---|
| 501 | 493 | /** |
|---|
| 502 | | - * snd_pcm_sync - set the PCM sync id |
|---|
| 494 | + * snd_pcm_set_sync - set the PCM sync id |
|---|
| 503 | 495 | * @substream: the pcm substream |
|---|
| 504 | 496 | * |
|---|
| 505 | 497 | * Sets the PCM sync identifier for the card. |
|---|
| .. | .. |
|---|
| 1462 | 1454 | |
|---|
| 1463 | 1455 | static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) |
|---|
| 1464 | 1456 | { |
|---|
| 1465 | | - static unsigned int pow2_sizes[] = { |
|---|
| 1457 | + static const unsigned int pow2_sizes[] = { |
|---|
| 1466 | 1458 | 1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7, |
|---|
| 1467 | 1459 | 1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15, |
|---|
| 1468 | 1460 | 1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23, |
|---|
| .. | .. |
|---|
| 2183 | 2175 | if (err < 0) |
|---|
| 2184 | 2176 | goto _end_unlock; |
|---|
| 2185 | 2177 | |
|---|
| 2178 | + runtime->twake = runtime->control->avail_min ? : 1; |
|---|
| 2179 | + if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) |
|---|
| 2180 | + snd_pcm_update_hw_ptr(substream); |
|---|
| 2181 | + |
|---|
| 2182 | + /* |
|---|
| 2183 | + * If size < start_threshold, wait indefinitely. Another |
|---|
| 2184 | + * thread may start capture |
|---|
| 2185 | + */ |
|---|
| 2186 | 2186 | if (!is_playback && |
|---|
| 2187 | 2187 | runtime->status->state == SNDRV_PCM_STATE_PREPARED && |
|---|
| 2188 | 2188 | size >= runtime->start_threshold) { |
|---|
| .. | .. |
|---|
| 2191 | 2191 | goto _end_unlock; |
|---|
| 2192 | 2192 | } |
|---|
| 2193 | 2193 | |
|---|
| 2194 | | - runtime->twake = runtime->control->avail_min ? : 1; |
|---|
| 2195 | | - if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) |
|---|
| 2196 | | - snd_pcm_update_hw_ptr(substream); |
|---|
| 2197 | 2194 | avail = snd_pcm_avail(substream); |
|---|
| 2195 | + |
|---|
| 2198 | 2196 | while (size > 0) { |
|---|
| 2199 | 2197 | snd_pcm_uframes_t frames, appl_ptr, appl_ofs; |
|---|
| 2200 | 2198 | snd_pcm_uframes_t cont; |
|---|
| .. | .. |
|---|
| 2223 | 2221 | if (frames > cont) |
|---|
| 2224 | 2222 | frames = cont; |
|---|
| 2225 | 2223 | if (snd_BUG_ON(!frames)) { |
|---|
| 2226 | | - runtime->twake = 0; |
|---|
| 2227 | | - snd_pcm_stream_unlock_irq(substream); |
|---|
| 2228 | | - return -EINVAL; |
|---|
| 2224 | + err = -EINVAL; |
|---|
| 2225 | + goto _end_unlock; |
|---|
| 2226 | + } |
|---|
| 2227 | + if (!atomic_inc_unless_negative(&runtime->buffer_accessing)) { |
|---|
| 2228 | + err = -EBUSY; |
|---|
| 2229 | + goto _end_unlock; |
|---|
| 2229 | 2230 | } |
|---|
| 2230 | 2231 | snd_pcm_stream_unlock_irq(substream); |
|---|
| 2231 | 2232 | err = writer(substream, appl_ofs, data, offset, frames, |
|---|
| 2232 | 2233 | transfer); |
|---|
| 2233 | 2234 | snd_pcm_stream_lock_irq(substream); |
|---|
| 2235 | + atomic_dec(&runtime->buffer_accessing); |
|---|
| 2234 | 2236 | if (err < 0) |
|---|
| 2235 | 2237 | goto _end_unlock; |
|---|
| 2236 | 2238 | err = pcm_accessible_state(runtime); |
|---|
| .. | .. |
|---|
| 2325 | 2327 | struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); |
|---|
| 2326 | 2328 | |
|---|
| 2327 | 2329 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
|---|
| 2328 | | - uinfo->count = 0; |
|---|
| 2329 | 2330 | uinfo->count = info->max_channels; |
|---|
| 2330 | 2331 | uinfo->value.integer.min = 0; |
|---|
| 2331 | 2332 | uinfo->value.integer.max = SNDRV_CHMAP_LAST; |
|---|
| .. | .. |
|---|
| 2349 | 2350 | if (!substream) |
|---|
| 2350 | 2351 | return -ENODEV; |
|---|
| 2351 | 2352 | memset(ucontrol->value.integer.value, 0, |
|---|
| 2352 | | - sizeof(ucontrol->value.integer.value)); |
|---|
| 2353 | + sizeof(long) * info->max_channels); |
|---|
| 2353 | 2354 | if (!substream->runtime) |
|---|
| 2354 | 2355 | return 0; /* no channels set */ |
|---|
| 2355 | 2356 | for (map = info->chmap; map->channels; map++) { |
|---|
| .. | .. |
|---|
| 2417 | 2418 | kfree(info); |
|---|
| 2418 | 2419 | } |
|---|
| 2419 | 2420 | |
|---|
| 2420 | | -static int pcm_volume_ctl_info(struct snd_kcontrol *kcontrol, |
|---|
| 2421 | | - struct snd_ctl_elem_info *uinfo) |
|---|
| 2422 | | -{ |
|---|
| 2423 | | - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
|---|
| 2424 | | - uinfo->count = 1; |
|---|
| 2425 | | - uinfo->value.integer.min = 0; |
|---|
| 2426 | | - uinfo->value.integer.max = 0x2000; |
|---|
| 2427 | | - return 0; |
|---|
| 2428 | | -} |
|---|
| 2429 | | - |
|---|
| 2430 | | -static void pcm_volume_ctl_private_free(struct snd_kcontrol *kcontrol) |
|---|
| 2431 | | -{ |
|---|
| 2432 | | - struct snd_pcm_volume *info = snd_kcontrol_chip(kcontrol); |
|---|
| 2433 | | - |
|---|
| 2434 | | - info->pcm->streams[info->stream].vol_kctl = NULL; |
|---|
| 2435 | | - kfree(info); |
|---|
| 2436 | | -} |
|---|
| 2437 | | - |
|---|
| 2438 | 2421 | /** |
|---|
| 2439 | 2422 | * snd_pcm_add_chmap_ctls - create channel-mapping control elements |
|---|
| 2440 | 2423 | * @pcm: the assigned PCM instance |
|---|
| .. | .. |
|---|
| 2496 | 2479 | return 0; |
|---|
| 2497 | 2480 | } |
|---|
| 2498 | 2481 | EXPORT_SYMBOL_GPL(snd_pcm_add_chmap_ctls); |
|---|
| 2499 | | - |
|---|
| 2500 | | -/** |
|---|
| 2501 | | - * snd_pcm_add_volume_ctls - create volume control elements |
|---|
| 2502 | | - * @pcm: the assigned PCM instance |
|---|
| 2503 | | - * @stream: stream direction |
|---|
| 2504 | | - * @max_length: the max length of the volume parameter of stream |
|---|
| 2505 | | - * @private_value: the value passed to each kcontrol's private_value field |
|---|
| 2506 | | - * @info_ret: store struct snd_pcm_volume instance if non-NULL |
|---|
| 2507 | | - * |
|---|
| 2508 | | - * Create volume control elements assigned to the given PCM stream(s). |
|---|
| 2509 | | - * Returns zero if succeed, or a negative error value. |
|---|
| 2510 | | - */ |
|---|
| 2511 | | -int snd_pcm_add_volume_ctls(struct snd_pcm *pcm, int stream, |
|---|
| 2512 | | - const struct snd_pcm_volume_elem *volume, |
|---|
| 2513 | | - int max_length, |
|---|
| 2514 | | - unsigned long private_value, |
|---|
| 2515 | | - struct snd_pcm_volume **info_ret) |
|---|
| 2516 | | -{ |
|---|
| 2517 | | - struct snd_pcm_volume *info; |
|---|
| 2518 | | - struct snd_kcontrol_new knew = { |
|---|
| 2519 | | - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
|---|
| 2520 | | - .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | |
|---|
| 2521 | | - SNDRV_CTL_ELEM_ACCESS_READWRITE, |
|---|
| 2522 | | - .info = pcm_volume_ctl_info, |
|---|
| 2523 | | - }; |
|---|
| 2524 | | - int err; |
|---|
| 2525 | | - int size; |
|---|
| 2526 | | - |
|---|
| 2527 | | - info = kzalloc(sizeof(*info), GFP_KERNEL); |
|---|
| 2528 | | - if (!info) |
|---|
| 2529 | | - return -ENOMEM; |
|---|
| 2530 | | - info->pcm = pcm; |
|---|
| 2531 | | - info->stream = stream; |
|---|
| 2532 | | - info->volume = volume; |
|---|
| 2533 | | - info->max_length = max_length; |
|---|
| 2534 | | - size = sizeof("Playback ") + sizeof(" Volume") + |
|---|
| 2535 | | - STRING_LENGTH_OF_INT*sizeof(char) + 1; |
|---|
| 2536 | | - knew.name = kzalloc(size, GFP_KERNEL); |
|---|
| 2537 | | - if (!knew.name) { |
|---|
| 2538 | | - kfree(info); |
|---|
| 2539 | | - return -ENOMEM; |
|---|
| 2540 | | - } |
|---|
| 2541 | | - if (stream == SNDRV_PCM_STREAM_PLAYBACK) |
|---|
| 2542 | | - snprintf((char *)knew.name, size, "%s %d %s", |
|---|
| 2543 | | - "Playback", pcm->device, "Volume"); |
|---|
| 2544 | | - else |
|---|
| 2545 | | - snprintf((char *)knew.name, size, "%s %d %s", |
|---|
| 2546 | | - "Capture", pcm->device, "Volume"); |
|---|
| 2547 | | - knew.device = pcm->device; |
|---|
| 2548 | | - knew.count = pcm->streams[stream].substream_count; |
|---|
| 2549 | | - knew.private_value = private_value; |
|---|
| 2550 | | - info->kctl = snd_ctl_new1(&knew, info); |
|---|
| 2551 | | - if (!info->kctl) { |
|---|
| 2552 | | - kfree(info); |
|---|
| 2553 | | - kfree(knew.name); |
|---|
| 2554 | | - return -ENOMEM; |
|---|
| 2555 | | - } |
|---|
| 2556 | | - info->kctl->private_free = pcm_volume_ctl_private_free; |
|---|
| 2557 | | - err = snd_ctl_add(pcm->card, info->kctl); |
|---|
| 2558 | | - if (err < 0) { |
|---|
| 2559 | | - kfree(info); |
|---|
| 2560 | | - kfree(knew.name); |
|---|
| 2561 | | - return -ENOMEM; |
|---|
| 2562 | | - } |
|---|
| 2563 | | - pcm->streams[stream].vol_kctl = info->kctl; |
|---|
| 2564 | | - if (info_ret) |
|---|
| 2565 | | - *info_ret = info; |
|---|
| 2566 | | - kfree(knew.name); |
|---|
| 2567 | | - return 0; |
|---|
| 2568 | | -} |
|---|
| 2569 | | -EXPORT_SYMBOL(snd_pcm_add_volume_ctls); |
|---|
| 2570 | | - |
|---|
| 2571 | | -static int pcm_usr_ctl_info(struct snd_kcontrol *kcontrol, |
|---|
| 2572 | | - struct snd_ctl_elem_info *uinfo) |
|---|
| 2573 | | -{ |
|---|
| 2574 | | - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
|---|
| 2575 | | - uinfo->count = MAX_USR_CTRL_CNT; |
|---|
| 2576 | | - uinfo->value.integer.min = 0; |
|---|
| 2577 | | - uinfo->value.integer.max = INT_MAX; |
|---|
| 2578 | | - return 0; |
|---|
| 2579 | | -} |
|---|
| 2580 | | - |
|---|
| 2581 | | -static void pcm_usr_ctl_private_free(struct snd_kcontrol *kcontrol) |
|---|
| 2582 | | -{ |
|---|
| 2583 | | - struct snd_pcm_usr *info = snd_kcontrol_chip(kcontrol); |
|---|
| 2584 | | - |
|---|
| 2585 | | - info->pcm->streams[info->stream].usr_kctl = NULL; |
|---|
| 2586 | | - kfree(info); |
|---|
| 2587 | | -} |
|---|
| 2588 | | - |
|---|
| 2589 | | -/** |
|---|
| 2590 | | - * snd_pcm_add_usr_ctls - create user control elements |
|---|
| 2591 | | - * @pcm: the assigned PCM instance |
|---|
| 2592 | | - * @stream: stream direction |
|---|
| 2593 | | - * @max_length: the max length of the user parameter of stream |
|---|
| 2594 | | - * @private_value: the value passed to each kcontrol's private_value field |
|---|
| 2595 | | - * @info_ret: store struct snd_pcm_usr instance if non-NULL |
|---|
| 2596 | | - * |
|---|
| 2597 | | - * Create usr control elements assigned to the given PCM stream(s). |
|---|
| 2598 | | - * Returns zero if succeed, or a negative error value. |
|---|
| 2599 | | - */ |
|---|
| 2600 | | -int snd_pcm_add_usr_ctls(struct snd_pcm *pcm, int stream, |
|---|
| 2601 | | - const struct snd_pcm_usr_elem *usr, |
|---|
| 2602 | | - int max_length, int max_kctrl_str_len, |
|---|
| 2603 | | - unsigned long private_value, |
|---|
| 2604 | | - struct snd_pcm_usr **info_ret) |
|---|
| 2605 | | -{ |
|---|
| 2606 | | - struct snd_pcm_usr *info; |
|---|
| 2607 | | - struct snd_kcontrol_new knew = { |
|---|
| 2608 | | - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
|---|
| 2609 | | - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, |
|---|
| 2610 | | - .info = pcm_usr_ctl_info, |
|---|
| 2611 | | - }; |
|---|
| 2612 | | - int err; |
|---|
| 2613 | | - char *buf; |
|---|
| 2614 | | - |
|---|
| 2615 | | - info = kzalloc(sizeof(*info), GFP_KERNEL); |
|---|
| 2616 | | - if (!info) |
|---|
| 2617 | | - return -ENOMEM; |
|---|
| 2618 | | - |
|---|
| 2619 | | - info->pcm = pcm; |
|---|
| 2620 | | - info->stream = stream; |
|---|
| 2621 | | - info->usr = usr; |
|---|
| 2622 | | - info->max_length = max_length; |
|---|
| 2623 | | - buf = kzalloc(max_kctrl_str_len, GFP_KERNEL); |
|---|
| 2624 | | - if (!buf) { |
|---|
| 2625 | | - pr_err("%s: buffer allocation failed\n", __func__); |
|---|
| 2626 | | - kfree(info); |
|---|
| 2627 | | - return -ENOMEM; |
|---|
| 2628 | | - } |
|---|
| 2629 | | - knew.name = buf; |
|---|
| 2630 | | - if (stream == SNDRV_PCM_STREAM_PLAYBACK) |
|---|
| 2631 | | - snprintf(buf, max_kctrl_str_len, "%s %d %s", |
|---|
| 2632 | | - "Playback", pcm->device, "User kcontrol"); |
|---|
| 2633 | | - else |
|---|
| 2634 | | - snprintf(buf, max_kctrl_str_len, "%s %d %s", |
|---|
| 2635 | | - "Capture", pcm->device, "User kcontrol"); |
|---|
| 2636 | | - knew.device = pcm->device; |
|---|
| 2637 | | - knew.count = pcm->streams[stream].substream_count; |
|---|
| 2638 | | - knew.private_value = private_value; |
|---|
| 2639 | | - info->kctl = snd_ctl_new1(&knew, info); |
|---|
| 2640 | | - if (!info->kctl) { |
|---|
| 2641 | | - kfree(info); |
|---|
| 2642 | | - kfree(knew.name); |
|---|
| 2643 | | - pr_err("%s: snd_ctl_new failed\n", __func__); |
|---|
| 2644 | | - return -ENOMEM; |
|---|
| 2645 | | - } |
|---|
| 2646 | | - info->kctl->private_free = pcm_usr_ctl_private_free; |
|---|
| 2647 | | - err = snd_ctl_add(pcm->card, info->kctl); |
|---|
| 2648 | | - if (err < 0) { |
|---|
| 2649 | | - kfree(info); |
|---|
| 2650 | | - kfree(knew.name); |
|---|
| 2651 | | - pr_err("%s: snd_ctl_add failed:%d\n", __func__, |
|---|
| 2652 | | - err); |
|---|
| 2653 | | - return -ENOMEM; |
|---|
| 2654 | | - } |
|---|
| 2655 | | - pcm->streams[stream].usr_kctl = info->kctl; |
|---|
| 2656 | | - if (info_ret) |
|---|
| 2657 | | - *info_ret = info; |
|---|
| 2658 | | - kfree(knew.name); |
|---|
| 2659 | | - return 0; |
|---|
| 2660 | | -} |
|---|
| 2661 | | -EXPORT_SYMBOL(snd_pcm_add_usr_ctls); |
|---|