• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/drivers/usb/gadget/
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	} else {
259		pcm_file = snd->filp->private_data;
260		snd->substream = pcm_file->substream;
261		snd->card = card;
262	}
263
264	return 0;
265}
266
267/**
268 * Close ALSA PCM and control device files
269 */
270static int gaudio_close_snd_dev(struct gaudio *gau)
271{
272	struct gaudio_snd_dev	*snd;
273
274	/* Close control device */
275	snd = &gau->control;
276	if (!IS_ERR(snd->filp))
277		filp_close(snd->filp, current->files);
278
279	/* Close PCM playback device and setup substream */
280	snd = &gau->playback;
281	if (!IS_ERR(snd->filp))
282		filp_close(snd->filp, current->files);
283
284	/* Close PCM capture device and setup substream */
285	snd = &gau->capture;
286	if (!IS_ERR(snd->filp))
287		filp_close(snd->filp, current->files);
288
289	return 0;
290}
291
292static struct gaudio *the_card;
293/**
294 * gaudio_setup - setup ALSA interface and preparing for USB transfer
295 *
296 * This sets up PCM, mixer or MIDI ALSA devices fore USB gadget using.
297 *
298 * Returns negative errno, or zero on success
299 */
300int __init gaudio_setup(struct gaudio *card)
301{
302	int	ret;
303
304	ret = gaudio_open_snd_dev(card);
305	if (ret)
306		ERROR(card, "we need at least one control device\n");
307
308	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