cregit-Linux how code gets into the kernel

Release 4.7 sound/usb/line6/pcm.c

Directory: sound/usb/line6
/*
 * Line 6 Linux USB driver
 *
 * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
 *
 *      This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License as
 *      published by the Free Software Foundation, version 2.
 *
 */

#include <linux/slab.h>
#include <linux/export.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>

#include "capture.h"
#include "driver.h"
#include "playback.h"

/* impulse response volume controls */

static int snd_line6_impulse_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = 255; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
takashi iwaitakashi iwai4078.43%150.00%
markus grabnermarkus grabner1121.57%150.00%
Total51100.00%2100.00%


static int snd_line6_impulse_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = line6pcm->impulse_volume; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
takashi iwaitakashi iwai2965.91%150.00%
markus grabnermarkus grabner1534.09%150.00%
Total44100.00%2100.00%


static int snd_line6_impulse_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); int value = ucontrol->value.integer.value[0]; int err; if (line6pcm->impulse_volume == value) return 0; line6pcm->impulse_volume = value; if (value > 0) { err = line6_pcm_acquire(line6pcm, LINE6_STREAM_IMPULSE); if (err < 0) { line6pcm->impulse_volume = 0; line6_pcm_release(line6pcm, LINE6_STREAM_IMPULSE); return err; } } else { line6_pcm_release(line6pcm, LINE6_STREAM_IMPULSE); } return 1; }

Contributors

PersonTokensPropCommitsCommitProp
takashi iwaitakashi iwai6153.51%350.00%
markus grabnermarkus grabner4741.23%233.33%
johannes thumshirnjohannes thumshirn65.26%116.67%
Total114100.00%6100.00%

/* impulse response period controls */
static int snd_line6_impulse_period_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = 2000; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
takashi iwaitakashi iwai4486.27%150.00%
markus grabnermarkus grabner713.73%150.00%
Total51100.00%2100.00%


static int snd_line6_impulse_period_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = line6pcm->impulse_period; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
takashi iwaitakashi iwai3477.27%150.00%
markus grabnermarkus grabner1022.73%150.00%
Total44100.00%2100.00%


static int snd_line6_impulse_period_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); int value = ucontrol->value.integer.value[0]; if (line6pcm->impulse_period == value) return 0; line6pcm->impulse_period = value; return 1; }

Contributors

PersonTokensPropCommitsCommitProp
takashi iwaitakashi iwai3558.33%133.33%
markus grabnermarkus grabner1626.67%133.33%
laurent navetlaurent navet915.00%133.33%
Total60100.00%3100.00%

/* Unlink all currently active URBs. */
static void line6_unlink_audio_urbs(struct snd_line6_pcm *line6pcm, struct line6_pcm_stream *pcms) { int i; for (i = 0; i < LINE6_ISO_BUFFERS; i++) { if (test_bit(i, &pcms->active_urbs)) { if (!test_and_set_bit(i, &pcms->unlink_urbs)) usb_unlink_urb(pcms->urbs[i]); } } }

Contributors

PersonTokensPropCommitsCommitProp
takashi iwaitakashi iwai71100.00%1100.00%
Total71100.00%1100.00%

/* Wait until unlinking of all currently active URBs has been finished. */
static void line6_wait_clear_audio_urbs(struct snd_line6_pcm *line6pcm, struct line6_pcm_stream *pcms) { int timeout = HZ; int i; int alive; do { alive = 0; for (i = 0; i < LINE6_ISO_BUFFERS; i++) { if (test_bit(i, &pcms->active_urbs)) alive++; } if (!alive) break; set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); } while (--timeout > 0); if (alive) dev_err(line6pcm->line6->ifcdev, "timeout: still %d active urbs..\n", alive); }

Contributors

PersonTokensPropCommitsCommitProp
takashi iwaitakashi iwai105100.00%2100.00%
Total105100.00%2100.00%


static inline struct line6_pcm_stream * get_stream(struct snd_line6_pcm *line6pcm, int direction) { return (direction == SNDRV_PCM_STREAM_PLAYBACK) ? &line6pcm->out : &line6pcm->in; }

Contributors

PersonTokensPropCommitsCommitProp
takashi iwaitakashi iwai34100.00%1100.00%
Total34100.00%1100.00%

/* allocate a buffer if not opened yet; * call this in line6pcm.state_change mutex */
static int line6_buffer_acquire(struct snd_line6_pcm *line6pcm, struct line6_pcm_stream *pstr, int type) { /* Invoked multiple times in a row so allocate once only */ if (!test_and_set_bit(type, &pstr->opened) && !pstr->buffer) { pstr->buffer = kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * line6pcm->max_packet_size, GFP_KERNEL); if (!pstr->buffer) return -ENOMEM; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
takashi iwaitakashi iwai71100.00%2100.00%
Total71100.00%2100.00%

/* free a buffer if all streams are closed; * call this in line6pcm.state_change mutex */
static void line6_buffer_release(struct snd_line6_pcm *line6pcm, struct line6_pcm_stream *pstr, int type) { clear_bit(type, &pstr->opened); if (!pstr->opened) { line6_wait_clear_audio_urbs(line6pcm, pstr); kfree(pstr->buffer); pstr->buffer = NULL; } }

Contributors

PersonTokensPropCommitsCommitProp
takashi iwaitakashi iwai5798.28%266.67%
markus grabnermarkus grabner11.72%133.33%
Total58100.00%3100.00%

/* start a PCM stream */
static int line6_stream_start(struct snd_line6_pcm *line6pcm, int direction, int type) { unsigned long flags; struct line6_pcm_stream *pstr = get_stream(line6pcm, direction); int ret = 0; spin_lock_irqsave(&pstr->lock, flags); if (!test_and_set_bit(type, &pstr->running) && !(pstr->active_urbs || pstr->unlink_urbs)) { pstr->count = 0; /* Submit all currently available URBs */ if (direction == SNDRV_PCM_STREAM_PLAYBACK) ret = line6_submit_audio_out_all_urbs(line6pcm); else ret = line6_submit_audio_in_all_urbs(line6pcm); } if (ret < 0) clear_bit(type, &pstr->running); spin_unlock_irqrestore(&pstr->lock, flags); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
takashi iwaitakashi iwai7960.31%555.56%
markus grabnermarkus grabner4534.35%333.33%
arnd bergmannarnd bergmann75.34%111.11%
Total131100.00%9100.00%

/* stop a PCM stream; this doesn't sync with the unlinked URBs */
static void line6_stream_stop(struct snd_line6_pcm *line6pcm, int direction, int type) { unsigned long flags; struct line6_pcm_stream *pstr = get_stream(line6pcm, direction); spin_lock_irqsave(&pstr->lock, flags); clear_bit(type, &pstr->running); if (!pstr->running) { line6_unlink_audio_urbs(line6pcm, pstr); if (direction == SNDRV_PCM_STREAM_CAPTURE) { line6pcm->prev_fbuf = NULL; line6pcm->prev_fsize = 0; } } spin_unlock_irqrestore(&pstr->lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
takashi iwaitakashi iwai9090.91%150.00%
markus grabnermarkus grabner99.09%150.00%
Total99100.00%2100.00%

/* common PCM trigger callback */
int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); struct snd_pcm_substream *s; int err; clear_bit(LINE6_FLAG_PREPARED, &line6pcm->flags); snd_pcm_group_for_each_entry(s, substream) { if (s->pcm->card != substream->pcm->card) continue; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: err = line6_stream_start(line6pcm, s->stream, LINE6_STREAM_PCM); if (err < 0) return err; break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: line6_stream_stop(line6pcm, s->stream, LINE6_STREAM_PCM); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: if (s->stream != SNDRV_PCM_STREAM_PLAYBACK) return -EINVAL; set_bit(LINE6_FLAG_PAUSE_PLAYBACK, &line6pcm->flags); break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (s->stream != SNDRV_PCM_STREAM_PLAYBACK) return -EINVAL; clear_bit(LINE6_FLAG_PAUSE_PLAYBACK, &line6pcm->flags); break; default: return -EINVAL; } } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
takashi iwaitakashi iwai15989.83%150.00%
markus grabnermarkus grabner1810.17%150.00%
Total177100.00%2100.00%

/* common PCM pointer callback */
snd_pcm_uframes_t snd_line6_pointer(struct snd_pcm_substream *substream) { struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); struct line6_pcm_stream *pstr = get_stream(line6pcm, substream->stream); return pstr->pos_done; }

Contributors

PersonTokensPropCommitsCommitProp
takashi iwaitakashi iwai39100.00%1100.00%
Total39100.00%1100.00%

/* Acquire and start duplex streams: * type is either LINE6_STREAM_IMPULSE or LINE6_STREAM_MONITOR */
int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type) { struct line6_pcm_stream *pstr; int ret = 0, dir; mutex_lock(&line6pcm->state_mutex); for (dir = 0; dir < 2; dir++) { pstr = get_stream(line6pcm, dir); ret = line6_buffer_acquire(line6pcm, pstr, type); if (ret < 0) goto error; if (!pstr->running) line6_wait_clear_audio_urbs(line6pcm, pstr); } for (dir = 0; dir < 2; dir++) { ret = line6_stream_start(line6pcm, dir, type); if (ret < 0) goto error; } error: mutex_unlock(&line6pcm->state_mutex); if (ret < 0) line6_pcm_release(line6pcm, type); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
takashi iwaitakashi iwai12280.26%125.00%
markus grabnermarkus grabner3019.74%375.00%
Total152100.00%4100.00%

EXPORT_SYMBOL_GPL(line6_pcm_acquire); /* Stop and release duplex streams */
void line6_pcm_release(struct snd_line6_pcm *line6pcm, int type) { struct line6_pcm_stream *pstr; int dir; mutex_lock(&line6pcm->state_mutex); for (dir = 0; dir < 2; dir++) line6_stream_stop(line6pcm, dir, type); for (dir = 0; dir < 2; dir++) { pstr = get_stream(line6pcm, dir); line6_buffer_release(line6pcm, pstr, type); } mutex_unlock(&line6pcm->state_mutex); }

Contributors

PersonTokensPropCommitsCommitProp
takashi iwaitakashi iwai6671.74%342.86%
markus grabnermarkus grabner1920.65%342.86%
arnd bergmannarnd bergmann77.61%114.29%
Total92100.00%7100.00%

EXPORT_SYMBOL_GPL(line6_pcm_release); /* common PCM hw_params callback */
int snd_line6_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { int ret; struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); struct line6_pcm_stream *pstr = get_stream(line6pcm, substream->stream); mutex_lock(&line6pcm->state_mutex); ret = line6_buffer_acquire(line6pcm, pstr, LINE6_STREAM_PCM); if (ret < 0) goto error; ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); if (ret < 0) { line6_buffer_release(line6pcm, pstr, LINE6_STREAM_PCM); goto error; } pstr->period = params_period_bytes(hw_params); error: mutex_unlock(&line6pcm->state_mutex); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
takashi iwaitakashi iwai9475.81%350.00%
markus grabnermarkus grabner3024.19%350.00%
Total124100.00%6100.00%

/* common PCM hw_free callback */
int snd_line6_hw_free(struct snd_pcm_substream *substream) { struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); struct line6_pcm_stream *pstr = get_stream(line6pcm, substream->stream); mutex_lock(&line6pcm->state_mutex); line6_buffer_release(line6pcm, pstr, LINE6_STREAM_PCM); mutex_unlock(&line6pcm->state_mutex); return snd_pcm_lib_free_pages(substream); }

Contributors

PersonTokensPropCommitsCommitProp
markus grabnermarkus grabner4163.08%240.00%
takashi iwaitakashi iwai2436.92%360.00%
Total65100.00%5100.00%

/* control info callback */
static int snd_line6_control_playback_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; uinfo->value.integer.min = 0; uinfo->value.integer.max = 256; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
markus grabnermarkus grabner51100.00%2100.00%
Total51100.00%2100.00%

/* control get callback */
static int snd_line6_control_playback_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { int i; struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); for (i = 0; i < 2; i++) ucontrol->value.integer.value[i] = line6pcm->volume_playback[i]; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
markus grabnermarkus grabner5892.06%266.67%
takashi iwaitakashi iwai57.94%133.33%
Total63100.00%3100.00%

/* control put callback */
static int snd_line6_control_playback_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { int i, changed = 0; struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); for (i = 0; i < 2; i++) if (line6pcm->volume_playback[i] != ucontrol->value.integer.value[i]) { line6pcm->volume_playback[i] = ucontrol->value.integer.value[i]; changed = 1; } return changed; }

Contributors

PersonTokensPropCommitsCommitProp
markus grabnermarkus grabner8894.62%266.67%
takashi iwaitakashi iwai55.38%133.33%
Total93100.00%3100.00%

/* control definition */ static struct snd_kcontrol_new line6_controls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "PCM Playback Volume", .info = snd_line6_control_playback_info, .get = snd_line6_control_playback_get, .put = snd_line6_control_playback_put }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Impulse Response Volume", .info = snd_line6_impulse_volume_info, .get = snd_line6_impulse_volume_get, .put = snd_line6_impulse_volume_put }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Impulse Response Period", .info = snd_line6_impulse_period_info, .get = snd_line6_impulse_period_get, .put = snd_line6_impulse_period_put }, }; /* Cleanup the PCM device. */
static void cleanup_urbs(struct line6_pcm_stream *pcms) { int i; for (i = 0; i < LINE6_ISO_BUFFERS; i++) { if (pcms->urbs[i]) { usb_kill_urb(pcms->urbs[i]); usb_free_urb(pcms->urbs[i]); } } }

Contributors

PersonTokensPropCommitsCommitProp
markus grabnermarkus grabner4473.33%125.00%
takashi iwaitakashi iwai1626.67%375.00%
Total60100.00%4100.00%


static void line6_cleanup_pcm(struct snd_pcm *pcm) { struct snd_line6_pcm *line6pcm = snd_pcm_chip(pcm); cleanup_urbs(&line6pcm->out); cleanup_urbs(&line6pcm->in); kfree(line6pcm); }

Contributors

PersonTokensPropCommitsCommitProp
takashi iwaitakashi iwai3071.43%375.00%
markus grabnermarkus grabner1228.57%125.00%
Total42100.00%4100.00%

/* create a PCM device */
static int snd_line6_new_pcm(struct usb_line6 *line6, struct snd_pcm **pcm_ret) { struct snd_pcm *pcm; int err; err = snd_pcm_new(line6->card, (char *)line6->properties->name, 0, 1, 1, pcm_ret); if (err < 0) return err; pcm = *pcm_ret; strcpy(pcm->name, line6->properties->name); /* set operators */ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_line6_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_line6_capture_ops); /* pre-allocation of buffers */ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, snd_dma_continuous_data (GFP_KERNEL), 64 * 1024, 128 * 1024); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
markus grabnermarkus grabner10987.90%133.33%
takashi iwaitakashi iwai118.87%133.33%
greg kroah-hartmangreg kroah-hartman43.23%133.33%
Total124100.00%3100.00%

/* Sync with PCM stream stops. */
void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm) { line6_unlink_audio_urbs(line6pcm, &line6pcm->out); line6_unlink_audio_urbs(line6pcm, &line6pcm->in); line6_wait_clear_audio_urbs(line6pcm, &line6pcm->out); line6_wait_clear_audio_urbs(line6pcm, &line6pcm->in); }

Contributors

PersonTokensPropCommitsCommitProp
takashi iwaitakashi iwai3264.00%150.00%
markus grabnermarkus grabner1836.00%150.00%
Total50100.00%2100.00%

/* Create and register the PCM device and mixer entries. Create URBs for playback and capture. */
int line6_init_pcm(struct usb_line6 *line6, struct line6_pcm_properties *properties) { int i, err; unsigned ep_read = line6->properties->ep_audio_r; unsigned ep_write = line6->properties->ep_audio_w; struct snd_pcm *pcm; struct snd_line6_pcm *line6pcm; if (!(line6->properties->capabilities & LINE6_CAP_PCM)) return 0; /* skip PCM initialization and report success */ err = snd_line6_new_pcm(line6, &pcm); if (err < 0) return err; line6pcm = kzalloc(sizeof(*line6pcm), GFP_KERNEL); if (!line6pcm) return -ENOMEM; mutex_init(&line6pcm->state_mutex); line6pcm->pcm = pcm; line6pcm->properties = properties; line6pcm->volume_playback[0] = line6pcm->volume_playback[1] = 255; line6pcm->volume_monitor = 255; line6pcm->line6 = line6; /* Read and write buffers are sized identically, so choose minimum */ line6pcm->max_packet_size = min( usb_maxpacket(line6->usbdev, usb_rcvisocpipe(line6->usbdev, ep_read), 0), usb_maxpacket(line6->usbdev, usb_sndisocpipe(line6->usbdev, ep_write), 1)); spin_lock_init(&line6pcm->out.lock); spin_lock_init(&line6pcm->in.lock); line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD; line6->line6pcm = line6pcm; pcm->private_data = line6pcm; pcm->private_free = line6_cleanup_pcm; err = line6_create_audio_out_urbs(line6pcm); if (err < 0) return err; err = line6_create_audio_in_urbs(line6pcm); if (err < 0) return err; /* mixer: */ for (i = 0; i < ARRAY_SIZE(line6_controls); i++) { err = snd_ctl_add(line6->card, snd_ctl_new1(&line6_controls[i], line6pcm)); if (err < 0) return err; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
markus grabnermarkus grabner17553.85%218.18%
takashi iwaitakashi iwai9930.46%436.36%
stefan hajnoczistefan hajnoczi237.08%19.09%
chris rorvickchris rorvick144.31%218.18%
greg kroah-hartmangreg kroah-hartman123.69%19.09%
l. alberto gimenezl. alberto gimenez20.62%19.09%
Total325100.00%11100.00%

EXPORT_SYMBOL_GPL(line6_init_pcm); /* prepare pcm callback */
int snd_line6_prepare(struct snd_pcm_substream *substream) { struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); struct line6_pcm_stream *pstr = get_stream(line6pcm, substream->stream); mutex_lock(&line6pcm->state_mutex); if (!pstr->running) line6_wait_clear_audio_urbs(line6pcm, pstr); if (!test_and_set_bit(LINE6_FLAG_PREPARED, &line6pcm->flags)) { line6pcm->out.count = 0; line6pcm->out.pos = 0; line6pcm->out.pos_done = 0; line6pcm->out.bytes = 0; line6pcm->in.count = 0; line6pcm->in.pos_done = 0; line6pcm->in.bytes = 0; } mutex_unlock(&line6pcm->state_mutex); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
markus grabnermarkus grabner7655.07%342.86%
takashi iwaitakashi iwai5842.03%342.86%
stefan hajnoczistefan hajnoczi42.90%114.29%
Total138100.00%7100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
takashi iwaitakashi iwai159859.56%1753.12%
markus grabnermarkus grabner99337.01%412.50%
stefan hajnoczistefan hajnoczi271.01%26.25%
greg kroah-hartmangreg kroah-hartman160.60%13.12%
chris rorvickchris rorvick150.56%39.38%
arnd bergmannarnd bergmann140.52%13.12%
laurent navetlaurent navet90.34%13.12%
johannes thumshirnjohannes thumshirn60.22%13.12%
tejun heotejun heo30.11%13.12%
l. alberto gimenezl. alberto gimenez20.07%13.12%
Total2683100.00%32100.00%
Directory: sound/usb/line6
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}