u_uac1_legacy.c (7230B)
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * u_uac1.c -- ALSA audio utilities for Gadget stack 4 * 5 * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org> 6 * Copyright (C) 2008 Analog Devices, Inc 7 */ 8 9#include <linux/kernel.h> 10#include <linux/module.h> 11#include <linux/slab.h> 12#include <linux/device.h> 13#include <linux/delay.h> 14#include <linux/ctype.h> 15#include <linux/random.h> 16#include <linux/syscalls.h> 17 18#include "u_uac1_legacy.h" 19 20/* 21 * This component encapsulates the ALSA devices for USB audio gadget 22 */ 23 24/*-------------------------------------------------------------------------*/ 25 26/* 27 * Some ALSA internal helper functions 28 */ 29static int snd_interval_refine_set(struct snd_interval *i, unsigned int val) 30{ 31 struct snd_interval t; 32 t.empty = 0; 33 t.min = t.max = val; 34 t.openmin = t.openmax = 0; 35 t.integer = 1; 36 return snd_interval_refine(i, &t); 37} 38 39static int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params, 40 snd_pcm_hw_param_t var, unsigned int val, 41 int dir) 42{ 43 int changed; 44 if (hw_is_mask(var)) { 45 struct snd_mask *m = hw_param_mask(params, var); 46 if (val == 0 && dir < 0) { 47 changed = -EINVAL; 48 snd_mask_none(m); 49 } else { 50 if (dir > 0) 51 val++; 52 else if (dir < 0) 53 val--; 54 changed = snd_mask_refine_set( 55 hw_param_mask(params, var), val); 56 } 57 } else if (hw_is_interval(var)) { 58 struct snd_interval *i = hw_param_interval(params, var); 59 if (val == 0 && dir < 0) { 60 changed = -EINVAL; 61 snd_interval_none(i); 62 } else if (dir == 0) 63 changed = snd_interval_refine_set(i, val); 64 else { 65 struct snd_interval t; 66 t.openmin = 1; 67 t.openmax = 1; 68 t.empty = 0; 69 t.integer = 0; 70 if (dir < 0) { 71 t.min = val - 1; 72 t.max = val; 73 } else { 74 t.min = val; 75 t.max = val+1; 76 } 77 changed = snd_interval_refine(i, &t); 78 } 79 } else 80 return -EINVAL; 81 if (changed) { 82 params->cmask |= 1 << var; 83 params->rmask |= 1 << var; 84 } 85 return changed; 86} 87/*-------------------------------------------------------------------------*/ 88 89/* 90 * Set default hardware params 91 */ 92static int playback_default_hw_params(struct gaudio_snd_dev *snd) 93{ 94 struct snd_pcm_substream *substream = snd->substream; 95 struct snd_pcm_hw_params *params; 96 snd_pcm_sframes_t result; 97 98 /* 99 * SNDRV_PCM_ACCESS_RW_INTERLEAVED, 100 * SNDRV_PCM_FORMAT_S16_LE 101 * CHANNELS: 2 102 * RATE: 48000 103 */ 104 snd->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; 105 snd->format = SNDRV_PCM_FORMAT_S16_LE; 106 snd->channels = 2; 107 snd->rate = 48000; 108 109 params = kzalloc(sizeof(*params), GFP_KERNEL); 110 if (!params) 111 return -ENOMEM; 112 113 _snd_pcm_hw_params_any(params); 114 _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS, 115 snd->access, 0); 116 _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT, 117 snd->format, 0); 118 _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS, 119 snd->channels, 0); 120 _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE, 121 snd->rate, 0); 122 123 snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL); 124 snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, params); 125 126 result = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL); 127 if (result < 0) { 128 ERROR(snd->card, 129 "Preparing sound card failed: %d\n", (int)result); 130 kfree(params); 131 return result; 132 } 133 134 /* Store the hardware parameters */ 135 snd->access = params_access(params); 136 snd->format = params_format(params); 137 snd->channels = params_channels(params); 138 snd->rate = params_rate(params); 139 140 kfree(params); 141 142 INFO(snd->card, 143 "Hardware params: access %x, format %x, channels %d, rate %d\n", 144 snd->access, snd->format, snd->channels, snd->rate); 145 146 return 0; 147} 148 149/* 150 * Playback audio buffer data by ALSA PCM device 151 */ 152size_t u_audio_playback(struct gaudio *card, void *buf, size_t count) 153{ 154 struct gaudio_snd_dev *snd = &card->playback; 155 struct snd_pcm_substream *substream = snd->substream; 156 struct snd_pcm_runtime *runtime = substream->runtime; 157 ssize_t result; 158 snd_pcm_sframes_t frames; 159 160try_again: 161 if (runtime->status->state == SNDRV_PCM_STATE_XRUN || 162 runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { 163 result = snd_pcm_kernel_ioctl(substream, 164 SNDRV_PCM_IOCTL_PREPARE, NULL); 165 if (result < 0) { 166 ERROR(card, "Preparing sound card failed: %d\n", 167 (int)result); 168 return result; 169 } 170 } 171 172 frames = bytes_to_frames(runtime, count); 173 result = snd_pcm_kernel_write(snd->substream, buf, frames); 174 if (result != frames) { 175 ERROR(card, "Playback error: %d\n", (int)result); 176 goto try_again; 177 } 178 179 return 0; 180} 181 182int u_audio_get_playback_channels(struct gaudio *card) 183{ 184 return card->playback.channels; 185} 186 187int u_audio_get_playback_rate(struct gaudio *card) 188{ 189 return card->playback.rate; 190} 191 192/* 193 * Open ALSA PCM and control device files 194 * Initial the PCM or control device 195 */ 196static int gaudio_open_snd_dev(struct gaudio *card) 197{ 198 struct snd_pcm_file *pcm_file; 199 struct gaudio_snd_dev *snd; 200 struct f_uac1_legacy_opts *opts; 201 char *fn_play, *fn_cap, *fn_cntl; 202 203 opts = container_of(card->func.fi, struct f_uac1_legacy_opts, 204 func_inst); 205 fn_play = opts->fn_play; 206 fn_cap = opts->fn_cap; 207 fn_cntl = opts->fn_cntl; 208 209 /* Open control device */ 210 snd = &card->control; 211 snd->filp = filp_open(fn_cntl, O_RDWR, 0); 212 if (IS_ERR(snd->filp)) { 213 int ret = PTR_ERR(snd->filp); 214 ERROR(card, "unable to open sound control device file: %s\n", 215 fn_cntl); 216 snd->filp = NULL; 217 return ret; 218 } 219 snd->card = card; 220 221 /* Open PCM playback device and setup substream */ 222 snd = &card->playback; 223 snd->filp = filp_open(fn_play, O_WRONLY, 0); 224 if (IS_ERR(snd->filp)) { 225 int ret = PTR_ERR(snd->filp); 226 227 ERROR(card, "No such PCM playback device: %s\n", fn_play); 228 snd->filp = NULL; 229 return ret; 230 } 231 pcm_file = snd->filp->private_data; 232 snd->substream = pcm_file->substream; 233 snd->card = card; 234 playback_default_hw_params(snd); 235 236 /* Open PCM capture device and setup substream */ 237 snd = &card->capture; 238 snd->filp = filp_open(fn_cap, O_RDONLY, 0); 239 if (IS_ERR(snd->filp)) { 240 ERROR(card, "No such PCM capture device: %s\n", fn_cap); 241 snd->substream = NULL; 242 snd->card = NULL; 243 snd->filp = NULL; 244 } else { 245 pcm_file = snd->filp->private_data; 246 snd->substream = pcm_file->substream; 247 snd->card = card; 248 } 249 250 return 0; 251} 252 253/* 254 * Close ALSA PCM and control device files 255 */ 256static int gaudio_close_snd_dev(struct gaudio *gau) 257{ 258 struct gaudio_snd_dev *snd; 259 260 /* Close control device */ 261 snd = &gau->control; 262 if (snd->filp) 263 filp_close(snd->filp, NULL); 264 265 /* Close PCM playback device and setup substream */ 266 snd = &gau->playback; 267 if (snd->filp) 268 filp_close(snd->filp, NULL); 269 270 /* Close PCM capture device and setup substream */ 271 snd = &gau->capture; 272 if (snd->filp) 273 filp_close(snd->filp, NULL); 274 275 return 0; 276} 277 278/* 279 * gaudio_setup - setup ALSA interface and preparing for USB transfer 280 * 281 * This sets up PCM, mixer or MIDI ALSA devices fore USB gadget using. 282 * 283 * Returns negative errno, or zero on success 284 */ 285int gaudio_setup(struct gaudio *card) 286{ 287 int ret; 288 289 ret = gaudio_open_snd_dev(card); 290 if (ret) 291 ERROR(card, "we need at least one control device\n"); 292 293 return ret; 294 295} 296 297/* 298 * gaudio_cleanup - remove ALSA device interface 299 * 300 * This is called to free all resources allocated by @gaudio_setup(). 301 */ 302void gaudio_cleanup(struct gaudio *the_card) 303{ 304 if (the_card) 305 gaudio_close_snd_dev(the_card); 306} 307