~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to drivers/usb/gadget/u_audio.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
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
 */
 
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
 
 
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
 */
 
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
 */
 
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
 
 
205
static int u_audio_get_playback_channels(struct gaudio *card)
 
206
{
 
207
        return card->playback.channels;
 
208
}
 
209
 
 
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
 */
 
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
 */
 
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
 */
 
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
 */
 
320
void gaudio_cleanup(void)
 
321
{
 
322
        if (the_card) {
 
323
                gaudio_close_snd_dev(the_card);
 
324
                the_card = NULL;
 
325
        }
 
326
}
 
327