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