1 /*
2  * u_uac1.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_uac1.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 
30 static char *fn_play = FILE_PCM_PLAYBACK;
31 module_param(fn_play, charp, S_IRUGO);
32 MODULE_PARM_DESC(fn_play, "Playback PCM device file name");
33 
34 static char *fn_cap = FILE_PCM_CAPTURE;
35 module_param(fn_cap, charp, S_IRUGO);
36 MODULE_PARM_DESC(fn_cap, "Capture PCM device file name");
37 
38 static char *fn_cntl = FILE_CONTROL;
39 module_param(fn_cntl, charp, S_IRUGO);
40 MODULE_PARM_DESC(fn_cntl, "Control device file name");
41 
42 /*-------------------------------------------------------------------------*/
43 
44 /**
45  * Some ALSA internal helper functions
46  */
snd_interval_refine_set(struct snd_interval * i,unsigned int val)47 static 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 
_snd_pcm_hw_param_set(struct snd_pcm_hw_params * params,snd_pcm_hw_param_t var,unsigned int val,int dir)57 static 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  */
playback_default_hw_params(struct gaudio_snd_dev * snd)110 static 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  */
u_audio_playback(struct gaudio * card,void * buf,size_t count)170 static 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 
179 try_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 
u_audio_get_playback_channels(struct gaudio * card)205 static int u_audio_get_playback_channels(struct gaudio *card)
206 {
207 	return card->playback.channels;
208 }
209 
u_audio_get_playback_rate(struct gaudio * card)210 static 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  */
gaudio_open_snd_dev(struct gaudio * card)219 static 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  */
gaudio_close_snd_dev(struct gaudio * gau)271 static 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 
293 static 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  */
gaudio_setup(struct gaudio * card)301 int __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  */
gaudio_cleanup(void)320 void gaudio_cleanup(void)
321 {
322 	if (the_card) {
323 		gaudio_close_snd_dev(the_card);
324 		the_card = NULL;
325 	}
326 }
327 
328