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