staticint 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;
}
/* Unlink all currently active URBs.
*/ staticvoid line6_unlink_audio_urbs(struct snd_line6_pcm *line6pcm, struct line6_pcm_stream *pcms)
{ int i;
for (i = 0; i < line6pcm->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]);
}
}
}
/* Wait until unlinking of all currently active URBs has been finished.
*/ staticvoid 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 < line6pcm->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);
}
/* allocate a buffer if not opened yet; * call this in line6pcm.state_mutex
*/ staticint line6_buffer_acquire(struct snd_line6_pcm *line6pcm, struct line6_pcm_stream *pstr, int direction, int type)
{ constint pkt_size =
(direction == SNDRV_PCM_STREAM_PLAYBACK) ?
line6pcm->max_packet_size_out :
line6pcm->max_packet_size_in;
/* Invoked multiple times in a row so allocate once only */ if (!test_and_set_bit(type, &pstr->opened) && !pstr->buffer) {
pstr->buffer =
kmalloc(array3_size(line6pcm->line6->iso_buffers,
LINE6_ISO_PACKETS, pkt_size),
GFP_KERNEL); if (!pstr->buffer) return -ENOMEM;
} return 0;
}
/* free a buffer if all streams are closed; * call this in line6pcm.state_mutex
*/ staticvoid 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;
}
}
/* start a PCM stream */ staticint line6_stream_start(struct snd_line6_pcm *line6pcm, int direction, int type)
{ unsignedlong 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);
}
/* stop a PCM stream; this doesn't sync with the unlinked URBs */ staticvoid line6_stream_stop(struct snd_line6_pcm *line6pcm, int direction, int type)
{ unsignedlong flags; struct line6_pcm_stream *pstr = get_stream(line6pcm, direction);
/* Acquire and optionally start duplex streams: * type is either LINE6_STREAM_IMPULSE or LINE6_STREAM_MONITOR
*/ int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type, bool start)
{ struct line6_pcm_stream *pstr; int ret = 0, dir;
/* TODO: We should assert SNDRV_PCM_STREAM_PLAYBACK/CAPTURE == 0/1 */
mutex_lock(&line6pcm->state_mutex); for (dir = 0; dir < 2; dir++) {
pstr = get_stream(line6pcm, dir);
ret = line6_buffer_acquire(line6pcm, pstr, dir, type); if (ret < 0) goto error; if (!pstr->running)
line6_wait_clear_audio_urbs(line6pcm, pstr);
} if (start) { 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;
}
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);
}
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, substream->stream,
LINE6_STREAM_PCM); if (ret < 0) goto error;
/* control get callback */ staticint 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;
}
/* control put callback */ staticint 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;
}
/* 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;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.