Linux Audio

Check our new training course

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