162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * u_uac1.c -- ALSA audio utilities for Gadget stack 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org> 662306a36Sopenharmony_ci * Copyright (C) 2008 Analog Devices, Inc 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/ctype.h> 1562306a36Sopenharmony_ci#include <linux/random.h> 1662306a36Sopenharmony_ci#include <linux/syscalls.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "u_uac1_legacy.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * This component encapsulates the ALSA devices for USB audio gadget 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* 2762306a36Sopenharmony_ci * Some ALSA internal helper functions 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_cistatic int snd_interval_refine_set(struct snd_interval *i, unsigned int val) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci struct snd_interval t; 3262306a36Sopenharmony_ci t.empty = 0; 3362306a36Sopenharmony_ci t.min = t.max = val; 3462306a36Sopenharmony_ci t.openmin = t.openmax = 0; 3562306a36Sopenharmony_ci t.integer = 1; 3662306a36Sopenharmony_ci return snd_interval_refine(i, &t); 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params, 4062306a36Sopenharmony_ci snd_pcm_hw_param_t var, unsigned int val, 4162306a36Sopenharmony_ci int dir) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci int changed; 4462306a36Sopenharmony_ci if (hw_is_mask(var)) { 4562306a36Sopenharmony_ci struct snd_mask *m = hw_param_mask(params, var); 4662306a36Sopenharmony_ci if (val == 0 && dir < 0) { 4762306a36Sopenharmony_ci changed = -EINVAL; 4862306a36Sopenharmony_ci snd_mask_none(m); 4962306a36Sopenharmony_ci } else { 5062306a36Sopenharmony_ci if (dir > 0) 5162306a36Sopenharmony_ci val++; 5262306a36Sopenharmony_ci else if (dir < 0) 5362306a36Sopenharmony_ci val--; 5462306a36Sopenharmony_ci changed = snd_mask_refine_set( 5562306a36Sopenharmony_ci hw_param_mask(params, var), val); 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci } else if (hw_is_interval(var)) { 5862306a36Sopenharmony_ci struct snd_interval *i = hw_param_interval(params, var); 5962306a36Sopenharmony_ci if (val == 0 && dir < 0) { 6062306a36Sopenharmony_ci changed = -EINVAL; 6162306a36Sopenharmony_ci snd_interval_none(i); 6262306a36Sopenharmony_ci } else if (dir == 0) 6362306a36Sopenharmony_ci changed = snd_interval_refine_set(i, val); 6462306a36Sopenharmony_ci else { 6562306a36Sopenharmony_ci struct snd_interval t; 6662306a36Sopenharmony_ci t.openmin = 1; 6762306a36Sopenharmony_ci t.openmax = 1; 6862306a36Sopenharmony_ci t.empty = 0; 6962306a36Sopenharmony_ci t.integer = 0; 7062306a36Sopenharmony_ci if (dir < 0) { 7162306a36Sopenharmony_ci t.min = val - 1; 7262306a36Sopenharmony_ci t.max = val; 7362306a36Sopenharmony_ci } else { 7462306a36Sopenharmony_ci t.min = val; 7562306a36Sopenharmony_ci t.max = val+1; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci changed = snd_interval_refine(i, &t); 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci } else 8062306a36Sopenharmony_ci return -EINVAL; 8162306a36Sopenharmony_ci if (changed) { 8262306a36Sopenharmony_ci params->cmask |= 1 << var; 8362306a36Sopenharmony_ci params->rmask |= 1 << var; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci return changed; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/* 9062306a36Sopenharmony_ci * Set default hardware params 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_cistatic int playback_default_hw_params(struct gaudio_snd_dev *snd) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci struct snd_pcm_substream *substream = snd->substream; 9562306a36Sopenharmony_ci struct snd_pcm_hw_params *params; 9662306a36Sopenharmony_ci snd_pcm_sframes_t result; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* 9962306a36Sopenharmony_ci * SNDRV_PCM_ACCESS_RW_INTERLEAVED, 10062306a36Sopenharmony_ci * SNDRV_PCM_FORMAT_S16_LE 10162306a36Sopenharmony_ci * CHANNELS: 2 10262306a36Sopenharmony_ci * RATE: 48000 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_ci snd->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; 10562306a36Sopenharmony_ci snd->format = SNDRV_PCM_FORMAT_S16_LE; 10662306a36Sopenharmony_ci snd->channels = 2; 10762306a36Sopenharmony_ci snd->rate = 48000; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci params = kzalloc(sizeof(*params), GFP_KERNEL); 11062306a36Sopenharmony_ci if (!params) 11162306a36Sopenharmony_ci return -ENOMEM; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci _snd_pcm_hw_params_any(params); 11462306a36Sopenharmony_ci _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS, 11562306a36Sopenharmony_ci snd->access, 0); 11662306a36Sopenharmony_ci _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT, 11762306a36Sopenharmony_ci snd->format, 0); 11862306a36Sopenharmony_ci _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS, 11962306a36Sopenharmony_ci snd->channels, 0); 12062306a36Sopenharmony_ci _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE, 12162306a36Sopenharmony_ci snd->rate, 0); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL); 12462306a36Sopenharmony_ci snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, params); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci result = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL); 12762306a36Sopenharmony_ci if (result < 0) { 12862306a36Sopenharmony_ci ERROR(snd->card, 12962306a36Sopenharmony_ci "Preparing sound card failed: %d\n", (int)result); 13062306a36Sopenharmony_ci kfree(params); 13162306a36Sopenharmony_ci return result; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* Store the hardware parameters */ 13562306a36Sopenharmony_ci snd->access = params_access(params); 13662306a36Sopenharmony_ci snd->format = params_format(params); 13762306a36Sopenharmony_ci snd->channels = params_channels(params); 13862306a36Sopenharmony_ci snd->rate = params_rate(params); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci kfree(params); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci INFO(snd->card, 14362306a36Sopenharmony_ci "Hardware params: access %x, format %x, channels %d, rate %d\n", 14462306a36Sopenharmony_ci snd->access, snd->format, snd->channels, snd->rate); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci return 0; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* 15062306a36Sopenharmony_ci * Playback audio buffer data by ALSA PCM device 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_cisize_t u_audio_playback(struct gaudio *card, void *buf, size_t count) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct gaudio_snd_dev *snd = &card->playback; 15562306a36Sopenharmony_ci struct snd_pcm_substream *substream = snd->substream; 15662306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 15762306a36Sopenharmony_ci ssize_t result; 15862306a36Sopenharmony_ci snd_pcm_sframes_t frames; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_citry_again: 16162306a36Sopenharmony_ci if (runtime->state == SNDRV_PCM_STATE_XRUN || 16262306a36Sopenharmony_ci runtime->state == SNDRV_PCM_STATE_SUSPENDED) { 16362306a36Sopenharmony_ci result = snd_pcm_kernel_ioctl(substream, 16462306a36Sopenharmony_ci SNDRV_PCM_IOCTL_PREPARE, NULL); 16562306a36Sopenharmony_ci if (result < 0) { 16662306a36Sopenharmony_ci ERROR(card, "Preparing sound card failed: %d\n", 16762306a36Sopenharmony_ci (int)result); 16862306a36Sopenharmony_ci return result; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci frames = bytes_to_frames(runtime, count); 17362306a36Sopenharmony_ci result = snd_pcm_kernel_write(snd->substream, buf, frames); 17462306a36Sopenharmony_ci if (result != frames) { 17562306a36Sopenharmony_ci ERROR(card, "Playback error: %d\n", (int)result); 17662306a36Sopenharmony_ci goto try_again; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return 0; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ciint u_audio_get_playback_channels(struct gaudio *card) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci return card->playback.channels; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ciint u_audio_get_playback_rate(struct gaudio *card) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci return card->playback.rate; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/* 19362306a36Sopenharmony_ci * Open ALSA PCM and control device files 19462306a36Sopenharmony_ci * Initial the PCM or control device 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_cistatic int gaudio_open_snd_dev(struct gaudio *card) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct snd_pcm_file *pcm_file; 19962306a36Sopenharmony_ci struct gaudio_snd_dev *snd; 20062306a36Sopenharmony_ci struct f_uac1_legacy_opts *opts; 20162306a36Sopenharmony_ci char *fn_play, *fn_cap, *fn_cntl; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci opts = container_of(card->func.fi, struct f_uac1_legacy_opts, 20462306a36Sopenharmony_ci func_inst); 20562306a36Sopenharmony_ci fn_play = opts->fn_play; 20662306a36Sopenharmony_ci fn_cap = opts->fn_cap; 20762306a36Sopenharmony_ci fn_cntl = opts->fn_cntl; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* Open control device */ 21062306a36Sopenharmony_ci snd = &card->control; 21162306a36Sopenharmony_ci snd->filp = filp_open(fn_cntl, O_RDWR, 0); 21262306a36Sopenharmony_ci if (IS_ERR(snd->filp)) { 21362306a36Sopenharmony_ci int ret = PTR_ERR(snd->filp); 21462306a36Sopenharmony_ci ERROR(card, "unable to open sound control device file: %s\n", 21562306a36Sopenharmony_ci fn_cntl); 21662306a36Sopenharmony_ci snd->filp = NULL; 21762306a36Sopenharmony_ci return ret; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci snd->card = card; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* Open PCM playback device and setup substream */ 22262306a36Sopenharmony_ci snd = &card->playback; 22362306a36Sopenharmony_ci snd->filp = filp_open(fn_play, O_WRONLY, 0); 22462306a36Sopenharmony_ci if (IS_ERR(snd->filp)) { 22562306a36Sopenharmony_ci int ret = PTR_ERR(snd->filp); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci ERROR(card, "No such PCM playback device: %s\n", fn_play); 22862306a36Sopenharmony_ci snd->filp = NULL; 22962306a36Sopenharmony_ci return ret; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci pcm_file = snd->filp->private_data; 23262306a36Sopenharmony_ci snd->substream = pcm_file->substream; 23362306a36Sopenharmony_ci snd->card = card; 23462306a36Sopenharmony_ci playback_default_hw_params(snd); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* Open PCM capture device and setup substream */ 23762306a36Sopenharmony_ci snd = &card->capture; 23862306a36Sopenharmony_ci snd->filp = filp_open(fn_cap, O_RDONLY, 0); 23962306a36Sopenharmony_ci if (IS_ERR(snd->filp)) { 24062306a36Sopenharmony_ci ERROR(card, "No such PCM capture device: %s\n", fn_cap); 24162306a36Sopenharmony_ci snd->substream = NULL; 24262306a36Sopenharmony_ci snd->card = NULL; 24362306a36Sopenharmony_ci snd->filp = NULL; 24462306a36Sopenharmony_ci } else { 24562306a36Sopenharmony_ci pcm_file = snd->filp->private_data; 24662306a36Sopenharmony_ci snd->substream = pcm_file->substream; 24762306a36Sopenharmony_ci snd->card = card; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci return 0; 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci/* 25462306a36Sopenharmony_ci * Close ALSA PCM and control device files 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_cistatic int gaudio_close_snd_dev(struct gaudio *gau) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct gaudio_snd_dev *snd; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* Close control device */ 26162306a36Sopenharmony_ci snd = &gau->control; 26262306a36Sopenharmony_ci if (snd->filp) 26362306a36Sopenharmony_ci filp_close(snd->filp, NULL); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* Close PCM playback device and setup substream */ 26662306a36Sopenharmony_ci snd = &gau->playback; 26762306a36Sopenharmony_ci if (snd->filp) 26862306a36Sopenharmony_ci filp_close(snd->filp, NULL); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* Close PCM capture device and setup substream */ 27162306a36Sopenharmony_ci snd = &gau->capture; 27262306a36Sopenharmony_ci if (snd->filp) 27362306a36Sopenharmony_ci filp_close(snd->filp, NULL); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci return 0; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci/* 27962306a36Sopenharmony_ci * gaudio_setup - setup ALSA interface and preparing for USB transfer 28062306a36Sopenharmony_ci * 28162306a36Sopenharmony_ci * This sets up PCM, mixer or MIDI ALSA devices fore USB gadget using. 28262306a36Sopenharmony_ci * 28362306a36Sopenharmony_ci * Returns negative errno, or zero on success 28462306a36Sopenharmony_ci */ 28562306a36Sopenharmony_ciint gaudio_setup(struct gaudio *card) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci int ret; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci ret = gaudio_open_snd_dev(card); 29062306a36Sopenharmony_ci if (ret) 29162306a36Sopenharmony_ci ERROR(card, "we need at least one control device\n"); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return ret; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci/* 29862306a36Sopenharmony_ci * gaudio_cleanup - remove ALSA device interface 29962306a36Sopenharmony_ci * 30062306a36Sopenharmony_ci * This is called to free all resources allocated by @gaudio_setup(). 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_civoid gaudio_cleanup(struct gaudio *the_card) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci if (the_card) 30562306a36Sopenharmony_ci gaudio_close_snd_dev(the_card); 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 308