blob: 9a55e5cf4cd8ed5686d13250143a60853877393b [file] [log] [blame]
Bryan Wuc6994e62009-06-03 09:17:58 -04001/*
Jassi Brar18b5b3b2012-02-02 21:59:53 +05302 * u_uac1.c -- ALSA audio utilities for Gadget stack
Bryan Wuc6994e62009-06-03 09:17:58 -04003 *
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>
Andrzej Pietrasiewiczf3a34062014-07-22 19:58:38 +020013#include <linux/module.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090014#include <linux/slab.h>
Bryan Wuc6994e62009-06-03 09:17:58 -040015#include <linux/device.h>
16#include <linux/delay.h>
17#include <linux/ctype.h>
18#include <linux/random.h>
19#include <linux/syscalls.h>
20
Jassi Brar18b5b3b2012-02-02 21:59:53 +053021#include "u_uac1.h"
Bryan Wuc6994e62009-06-03 09:17:58 -040022
23/*
24 * This component encapsulates the ALSA devices for USB audio gadget
25 */
26
Andrzej Pietrasiewiczf3a34062014-07-22 19:58:38 +020027#ifdef USBF_UAC1_INCLUDED
Bryan Wuc6994e62009-06-03 09:17:58 -040028#define FILE_PCM_PLAYBACK "/dev/snd/pcmC0D0p"
29#define FILE_PCM_CAPTURE "/dev/snd/pcmC0D0c"
30#define FILE_CONTROL "/dev/snd/controlC0"
31
32static char *fn_play = FILE_PCM_PLAYBACK;
33module_param(fn_play, charp, S_IRUGO);
34MODULE_PARM_DESC(fn_play, "Playback PCM device file name");
35
36static char *fn_cap = FILE_PCM_CAPTURE;
37module_param(fn_cap, charp, S_IRUGO);
38MODULE_PARM_DESC(fn_cap, "Capture PCM device file name");
39
40static char *fn_cntl = FILE_CONTROL;
41module_param(fn_cntl, charp, S_IRUGO);
42MODULE_PARM_DESC(fn_cntl, "Control device file name");
Andrzej Pietrasiewiczf3a34062014-07-22 19:58:38 +020043#endif
Bryan Wuc6994e62009-06-03 09:17:58 -040044/*-------------------------------------------------------------------------*/
45
46/**
47 * Some ALSA internal helper functions
48 */
49static int snd_interval_refine_set(struct snd_interval *i, unsigned int val)
50{
51 struct snd_interval t;
52 t.empty = 0;
53 t.min = t.max = val;
54 t.openmin = t.openmax = 0;
55 t.integer = 1;
56 return snd_interval_refine(i, &t);
57}
58
59static int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params,
60 snd_pcm_hw_param_t var, unsigned int val,
61 int dir)
62{
63 int changed;
64 if (hw_is_mask(var)) {
65 struct snd_mask *m = hw_param_mask(params, var);
66 if (val == 0 && dir < 0) {
67 changed = -EINVAL;
68 snd_mask_none(m);
69 } else {
70 if (dir > 0)
71 val++;
72 else if (dir < 0)
73 val--;
74 changed = snd_mask_refine_set(
75 hw_param_mask(params, var), val);
76 }
77 } else if (hw_is_interval(var)) {
78 struct snd_interval *i = hw_param_interval(params, var);
79 if (val == 0 && dir < 0) {
80 changed = -EINVAL;
81 snd_interval_none(i);
82 } else if (dir == 0)
83 changed = snd_interval_refine_set(i, val);
84 else {
85 struct snd_interval t;
86 t.openmin = 1;
87 t.openmax = 1;
88 t.empty = 0;
89 t.integer = 0;
90 if (dir < 0) {
91 t.min = val - 1;
92 t.max = val;
93 } else {
94 t.min = val;
95 t.max = val+1;
96 }
97 changed = snd_interval_refine(i, &t);
98 }
99 } else
100 return -EINVAL;
101 if (changed) {
102 params->cmask |= 1 << var;
103 params->rmask |= 1 << var;
104 }
105 return changed;
106}
107/*-------------------------------------------------------------------------*/
108
109/**
110 * Set default hardware params
111 */
112static int playback_default_hw_params(struct gaudio_snd_dev *snd)
113{
114 struct snd_pcm_substream *substream = snd->substream;
115 struct snd_pcm_hw_params *params;
116 snd_pcm_sframes_t result;
117
118 /*
119 * SNDRV_PCM_ACCESS_RW_INTERLEAVED,
120 * SNDRV_PCM_FORMAT_S16_LE
121 * CHANNELS: 2
122 * RATE: 48000
123 */
124 snd->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
125 snd->format = SNDRV_PCM_FORMAT_S16_LE;
126 snd->channels = 2;
127 snd->rate = 48000;
128
129 params = kzalloc(sizeof(*params), GFP_KERNEL);
130 if (!params)
131 return -ENOMEM;
132
133 _snd_pcm_hw_params_any(params);
134 _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS,
135 snd->access, 0);
136 _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT,
137 snd->format, 0);
138 _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS,
139 snd->channels, 0);
140 _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE,
141 snd->rate, 0);
142
143 snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
144 snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, params);
145
146 result = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL);
147 if (result < 0) {
148 ERROR(snd->card,
149 "Preparing sound card failed: %d\n", (int)result);
150 kfree(params);
151 return result;
152 }
153
154 /* Store the hardware parameters */
155 snd->access = params_access(params);
156 snd->format = params_format(params);
157 snd->channels = params_channels(params);
158 snd->rate = params_rate(params);
159
160 kfree(params);
161
162 INFO(snd->card,
163 "Hardware params: access %x, format %x, channels %d, rate %d\n",
164 snd->access, snd->format, snd->channels, snd->rate);
165
166 return 0;
167}
168
169/**
170 * Playback audio buffer data by ALSA PCM device
171 */
Andrzej Pietrasiewiczf3a34062014-07-22 19:58:38 +0200172size_t u_audio_playback(struct gaudio *card, void *buf, size_t count)
Bryan Wuc6994e62009-06-03 09:17:58 -0400173{
174 struct gaudio_snd_dev *snd = &card->playback;
175 struct snd_pcm_substream *substream = snd->substream;
176 struct snd_pcm_runtime *runtime = substream->runtime;
177 mm_segment_t old_fs;
178 ssize_t result;
179 snd_pcm_sframes_t frames;
180
181try_again:
182 if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
183 runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
184 result = snd_pcm_kernel_ioctl(substream,
185 SNDRV_PCM_IOCTL_PREPARE, NULL);
186 if (result < 0) {
187 ERROR(card, "Preparing sound card failed: %d\n",
188 (int)result);
189 return result;
190 }
191 }
192
193 frames = bytes_to_frames(runtime, count);
194 old_fs = get_fs();
195 set_fs(KERNEL_DS);
Jingoo Han24198312013-08-05 12:14:17 +0900196 result = snd_pcm_lib_write(snd->substream, (void __user *)buf, frames);
Bryan Wuc6994e62009-06-03 09:17:58 -0400197 if (result != frames) {
198 ERROR(card, "Playback error: %d\n", (int)result);
199 set_fs(old_fs);
200 goto try_again;
201 }
202 set_fs(old_fs);
203
204 return 0;
205}
206
Andrzej Pietrasiewiczf3a34062014-07-22 19:58:38 +0200207int u_audio_get_playback_channels(struct gaudio *card)
Bryan Wuc6994e62009-06-03 09:17:58 -0400208{
209 return card->playback.channels;
210}
211
Andrzej Pietrasiewiczf3a34062014-07-22 19:58:38 +0200212int u_audio_get_playback_rate(struct gaudio *card)
Bryan Wuc6994e62009-06-03 09:17:58 -0400213{
214 return card->playback.rate;
215}
216
217/**
218 * Open ALSA PCM and control device files
219 * Initial the PCM or control device
220 */
221static int gaudio_open_snd_dev(struct gaudio *card)
222{
223 struct snd_pcm_file *pcm_file;
224 struct gaudio_snd_dev *snd;
Andrzej Pietrasiewiczf3a34062014-07-22 19:58:38 +0200225#ifndef USBF_UAC1_INCLUDED
226 struct f_uac1_opts *opts;
227 char *fn_play, *fn_cap, *fn_cntl;
228
229 opts = container_of(card->func.fi, struct f_uac1_opts, func_inst);
230 fn_play = opts->fn_play;
231 fn_cap = opts->fn_cap;
232 fn_cntl = opts->fn_cntl;
233#endif
Bryan Wuc6994e62009-06-03 09:17:58 -0400234
235 if (!card)
236 return -ENODEV;
237
238 /* Open control device */
239 snd = &card->control;
240 snd->filp = filp_open(fn_cntl, O_RDWR, 0);
241 if (IS_ERR(snd->filp)) {
242 int ret = PTR_ERR(snd->filp);
243 ERROR(card, "unable to open sound control device file: %s\n",
244 fn_cntl);
245 snd->filp = NULL;
246 return ret;
247 }
248 snd->card = card;
249
250 /* Open PCM playback device and setup substream */
251 snd = &card->playback;
252 snd->filp = filp_open(fn_play, O_WRONLY, 0);
253 if (IS_ERR(snd->filp)) {
Dan Carpenter29240e22013-02-28 07:44:38 +0300254 int ret = PTR_ERR(snd->filp);
255
Bryan Wuc6994e62009-06-03 09:17:58 -0400256 ERROR(card, "No such PCM playback device: %s\n", fn_play);
257 snd->filp = NULL;
Dan Carpenter29240e22013-02-28 07:44:38 +0300258 return ret;
Bryan Wuc6994e62009-06-03 09:17:58 -0400259 }
260 pcm_file = snd->filp->private_data;
261 snd->substream = pcm_file->substream;
262 snd->card = card;
263 playback_default_hw_params(snd);
264
265 /* Open PCM capture device and setup substream */
266 snd = &card->capture;
267 snd->filp = filp_open(fn_cap, O_RDONLY, 0);
268 if (IS_ERR(snd->filp)) {
269 ERROR(card, "No such PCM capture device: %s\n", fn_cap);
Robin Callendere792b1b2009-08-02 11:38:58 -0700270 snd->substream = NULL;
271 snd->card = NULL;
Richard Röjforsdf4fede2010-12-01 11:53:00 +0100272 snd->filp = NULL;
Robin Callendere792b1b2009-08-02 11:38:58 -0700273 } else {
274 pcm_file = snd->filp->private_data;
275 snd->substream = pcm_file->substream;
276 snd->card = card;
Bryan Wuc6994e62009-06-03 09:17:58 -0400277 }
Bryan Wuc6994e62009-06-03 09:17:58 -0400278
279 return 0;
280}
281
282/**
283 * Close ALSA PCM and control device files
284 */
285static int gaudio_close_snd_dev(struct gaudio *gau)
286{
287 struct gaudio_snd_dev *snd;
288
289 /* Close control device */
290 snd = &gau->control;
Richard Röjforsdf4fede2010-12-01 11:53:00 +0100291 if (snd->filp)
Al Viro20818a02012-07-22 21:23:33 +0400292 filp_close(snd->filp, NULL);
Bryan Wuc6994e62009-06-03 09:17:58 -0400293
294 /* Close PCM playback device and setup substream */
295 snd = &gau->playback;
Richard Röjforsdf4fede2010-12-01 11:53:00 +0100296 if (snd->filp)
Al Viro20818a02012-07-22 21:23:33 +0400297 filp_close(snd->filp, NULL);
Bryan Wuc6994e62009-06-03 09:17:58 -0400298
299 /* Close PCM capture device and setup substream */
300 snd = &gau->capture;
Richard Röjforsdf4fede2010-12-01 11:53:00 +0100301 if (snd->filp)
Al Viro20818a02012-07-22 21:23:33 +0400302 filp_close(snd->filp, NULL);
Bryan Wuc6994e62009-06-03 09:17:58 -0400303
304 return 0;
305}
306
Andrzej Pietrasiewiczf3a34062014-07-22 19:58:38 +0200307#ifdef USBF_UAC1_INCLUDED
Cliff Caifeef1d92009-12-09 22:28:39 -0500308static struct gaudio *the_card;
Andrzej Pietrasiewiczf3a34062014-07-22 19:58:38 +0200309#endif
Bryan Wuc6994e62009-06-03 09:17:58 -0400310/**
311 * gaudio_setup - setup ALSA interface and preparing for USB transfer
312 *
313 * This sets up PCM, mixer or MIDI ALSA devices fore USB gadget using.
314 *
315 * Returns negative errno, or zero on success
316 */
Andrzej Pietrasiewiczf3a34062014-07-22 19:58:38 +0200317int gaudio_setup(struct gaudio *card)
Bryan Wuc6994e62009-06-03 09:17:58 -0400318{
319 int ret;
320
321 ret = gaudio_open_snd_dev(card);
322 if (ret)
323 ERROR(card, "we need at least one control device\n");
Andrzej Pietrasiewiczf3a34062014-07-22 19:58:38 +0200324#ifdef USBF_UAC1_INCLUDED
Richard Röjforsdf4fede2010-12-01 11:53:00 +0100325 else if (!the_card)
Cliff Caifeef1d92009-12-09 22:28:39 -0500326 the_card = card;
Andrzej Pietrasiewiczf3a34062014-07-22 19:58:38 +0200327#endif
Cliff Caifeef1d92009-12-09 22:28:39 -0500328
Bryan Wuc6994e62009-06-03 09:17:58 -0400329 return ret;
330
331}
332
333/**
334 * gaudio_cleanup - remove ALSA device interface
335 *
336 * This is called to free all resources allocated by @gaudio_setup().
337 */
Andrzej Pietrasiewiczf3a34062014-07-22 19:58:38 +0200338#ifdef USBF_UAC1_INCLUDED
Cliff Caifeef1d92009-12-09 22:28:39 -0500339void gaudio_cleanup(void)
Andrzej Pietrasiewiczf3a34062014-07-22 19:58:38 +0200340#else
341void gaudio_cleanup(struct gaudio *the_card)
342#endif
Bryan Wuc6994e62009-06-03 09:17:58 -0400343{
Cliff Caifeef1d92009-12-09 22:28:39 -0500344 if (the_card) {
345 gaudio_close_snd_dev(the_card);
Andrzej Pietrasiewiczf3a34062014-07-22 19:58:38 +0200346#ifdef USBF_UAC1_INCLUDED
Cliff Caifeef1d92009-12-09 22:28:39 -0500347 the_card = NULL;
Andrzej Pietrasiewiczf3a34062014-07-22 19:58:38 +0200348#endif
Cliff Caifeef1d92009-12-09 22:28:39 -0500349 }
Bryan Wuc6994e62009-06-03 09:17:58 -0400350}
351