/* 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; }
/* Toneport device disconnected. */ void line6_toneport_disconnect(struct usb_interface *interface) { struct usb_line6_toneport *toneport; if (interface == NULL) return; toneport = usb_get_intfdata(interface); del_timer_sync(&toneport->timer); if (toneport_has_led(toneport->line6.usbdev->descriptor.idProduct)) { device_remove_file(&interface->dev, &dev_attr_led_red); device_remove_file(&interface->dev, &dev_attr_led_green); } if (toneport != NULL) { struct snd_line6_pcm *line6pcm = toneport->line6.line6pcm; if (line6pcm != NULL) { line6_pcm_release(line6pcm, LINE6_BITS_PCM_MONITOR); line6_pcm_disconnect(line6pcm); } } toneport_destruct(interface); }
/* 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; }
/* close capture callback */ static int snd_line6_capture_close(struct snd_pcm_substream *substream) { struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); line6_pcm_release(line6pcm, LINE6_STREAM_CAPTURE_HELPER); return 0; }
/* trigger callback */ int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd) { int err; switch (cmd) { case SNDRV_PCM_TRIGGER_START: #ifdef CONFIG_PM case SNDRV_PCM_TRIGGER_RESUME: #endif err = line6_pcm_acquire(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_STREAM); if (err < 0) return err; break; case SNDRV_PCM_TRIGGER_STOP: #ifdef CONFIG_PM case SNDRV_PCM_TRIGGER_SUSPEND: #endif err = line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_STREAM); if (err < 0) return err; break; default: return -EINVAL; } return 0; }
/* hw_params capture callback */ static int snd_line6_capture_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); /* -- Florian Demski [FD] */ /* don't ask me why, but this fixes the bug on my machine */ if (line6pcm == NULL) { if (substream->pcm == NULL) return -ENOMEM; if (substream->pcm->private_data == NULL) return -ENOMEM; substream->private_data = substream->pcm->private_data; line6pcm = snd_pcm_substream_chip(substream); } /* -- [FD] end */ ret = line6_pcm_acquire(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER); if (ret < 0) return ret; ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); if (ret < 0) { line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER); return ret; } line6pcm->period_in = params_period_bytes(hw_params); return 0; }
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; }
/* monitor put callback */ static int snd_toneport_monitor_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); if (ucontrol->value.integer.value[0] == line6pcm->volume_monitor) return 0; line6pcm->volume_monitor = ucontrol->value.integer.value[0]; if (line6pcm->volume_monitor > 0) line6_pcm_acquire(line6pcm, LINE6_BITS_PCM_MONITOR); else line6_pcm_release(line6pcm, LINE6_BITS_PCM_MONITOR); return 1; }
/* "write" request on "impulse_volume" special file. */ static ssize_t pcm_set_impulse_volume(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct snd_line6_pcm *line6pcm = dev2pcm(dev); int value; int ret; ret = kstrtoint(buf, 10, &value); if (ret < 0) return ret; line6pcm->impulse_volume = value; if (value > 0) line6_pcm_acquire(line6pcm, LINE6_BITS_PCM_IMPULSE); else line6_pcm_release(line6pcm, LINE6_BITS_PCM_IMPULSE); return count; }
int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int channels) { unsigned long flags_old, flags_new, flags_final; int err; do { flags_old = ACCESS_ONCE(line6pcm->flags); flags_new = flags_old | channels; } while (cmpxchg(&line6pcm->flags, flags_old, flags_new) != flags_old); flags_final = flags_old; line6pcm->prev_fbuf = NULL; if (test_flags(flags_old, flags_new, LINE6_BITS_CAPTURE_BUFFER)) { /* Invoked multiple times in a row so allocate once only */ if (!line6pcm->buffer_in) { line6pcm->buffer_in = kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * line6pcm->max_packet_size, GFP_KERNEL); if (!line6pcm->buffer_in) { err = -ENOMEM; goto pcm_acquire_error; } flags_final |= channels & LINE6_BITS_CAPTURE_BUFFER; } } if (test_flags(flags_old, flags_new, LINE6_BITS_CAPTURE_STREAM)) { /* Waiting for completion of active URBs in the stop handler is a bug, we therefore report an error if capturing is restarted too soon. */ if (line6pcm->active_urb_in | line6pcm->unlink_urb_in) { dev_err(line6pcm->line6->ifcdev, "Device not yet ready\n"); return -EBUSY; } line6pcm->count_in = 0; line6pcm->prev_fsize = 0; err = line6_submit_audio_in_all_urbs(line6pcm); if (err < 0) goto pcm_acquire_error; flags_final |= channels & LINE6_BITS_CAPTURE_STREAM; } if (test_flags(flags_old, flags_new, LINE6_BITS_PLAYBACK_BUFFER)) { /* Invoked multiple times in a row so allocate once only */ if (!line6pcm->buffer_out) { line6pcm->buffer_out = kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * line6pcm->max_packet_size, GFP_KERNEL); if (!line6pcm->buffer_out) { err = -ENOMEM; goto pcm_acquire_error; } flags_final |= channels & LINE6_BITS_PLAYBACK_BUFFER; } } if (test_flags(flags_old, flags_new, LINE6_BITS_PLAYBACK_STREAM)) { /* See comment above regarding PCM restart. */ if (line6pcm->active_urb_out | line6pcm->unlink_urb_out) { dev_err(line6pcm->line6->ifcdev, "Device not yet ready\n"); return -EBUSY; } line6pcm->count_out = 0; err = line6_submit_audio_out_all_urbs(line6pcm); if (err < 0) goto pcm_acquire_error; flags_final |= channels & LINE6_BITS_PLAYBACK_STREAM; } return 0; pcm_acquire_error: /* If not all requested resources/streams could be obtained, release those which were successfully obtained (if any). */ line6_pcm_release(line6pcm, flags_final & channels); return err; }
/* hw_free capture callback */ static int snd_line6_capture_hw_free(struct snd_pcm_substream *substream) { struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER); return snd_pcm_lib_free_pages(substream); }