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