+#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE
+
+static struct snd_line6_pcm* dev2pcm(struct device *dev)
+{
+ struct usb_interface *interface = to_usb_interface(dev);
+ struct usb_line6 *line6 = usb_get_intfdata(interface);
+ struct snd_line6_pcm *line6pcm = line6->line6pcm;
+ return line6pcm;
+}
+
+/*
+ "read" request on "impulse_volume" special file.
+*/
+static ssize_t pcm_get_impulse_volume(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", dev2pcm(dev)->impulse_volume);
+}
+
+/*
+ "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 = simple_strtoul(buf, NULL, 10);
+ line6pcm->impulse_volume = value;
+
+ if(value > 0)
+ line6_pcm_start(line6pcm, MASK_PCM_IMPULSE);
+ else
+ line6_pcm_stop(line6pcm, MASK_PCM_IMPULSE);
+
+ return count;
+}
+
+/*
+ "read" request on "impulse_period" special file.
+*/
+static ssize_t pcm_get_impulse_period(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", dev2pcm(dev)->impulse_period);
+}
+
+/*
+ "write" request on "impulse_period" special file.
+*/
+static ssize_t pcm_set_impulse_period(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ dev2pcm(dev)->impulse_period = simple_strtoul(buf, NULL, 10);
+ return count;
+}
+
+static DEVICE_ATTR(impulse_volume, S_IWUGO | S_IRUGO, pcm_get_impulse_volume, pcm_set_impulse_volume);
+static DEVICE_ATTR(impulse_period, S_IWUGO | S_IRUGO, pcm_get_impulse_period, pcm_set_impulse_period);
+
+#endif
+
+int line6_pcm_start(struct snd_line6_pcm *line6pcm, int channels)
+{
+ unsigned long flags_old = __sync_fetch_and_or(&line6pcm->flags, channels);
+ unsigned long flags_new = flags_old | channels;
+ int err = 0;
+
+#if LINE6_BACKUP_MONITOR_SIGNAL
+ if (!(line6pcm->line6->properties->capabilities & LINE6_BIT_HWMON)) {
+ line6pcm->prev_fbuf = kmalloc(LINE6_ISO_PACKETS * line6pcm->max_packet_size, GFP_KERNEL);
+
+ if (!line6pcm->prev_fbuf) {
+ dev_err(line6pcm->line6->ifcdev, "cannot malloc monitor buffer\n");
+ return -ENOMEM;
+ }
+ }
+#else
+ line6pcm->prev_fbuf = NULL;
+#endif
+
+ if (((flags_old & MASK_CAPTURE) == 0) &&
+ ((flags_new & MASK_CAPTURE) != 0)) {
+ /*
+ 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)
+ return -EBUSY;
+
+ line6pcm->buffer_in = kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * line6pcm->max_packet_size, GFP_KERNEL);
+
+ if (!line6pcm->buffer_in) {
+ dev_err(line6pcm->line6->ifcdev, "cannot malloc capture buffer\n");
+ return -ENOMEM;
+ }
+
+ line6pcm->count_in = 0;
+ line6pcm->prev_fsize = 0;
+ err = line6_submit_audio_in_all_urbs(line6pcm);
+
+ if (err < 0) {
+ __sync_fetch_and_and(&line6pcm->flags, ~channels);
+ return err;
+ }
+ }
+
+ if (((flags_old & MASK_PLAYBACK) == 0) &&
+ ((flags_new & MASK_PLAYBACK) != 0)) {
+ /*
+ See comment above regarding PCM restart.
+ */
+ if(line6pcm->active_urb_out | line6pcm->unlink_urb_out)
+ return -EBUSY;
+
+ line6pcm->buffer_out = kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * line6pcm->max_packet_size, GFP_KERNEL);
+
+ if (!line6pcm->buffer_out) {
+ dev_err(line6pcm->line6->ifcdev, "cannot malloc playback buffer\n");
+ return -ENOMEM;
+ }
+
+ line6pcm->count_out = 0;
+ err = line6_submit_audio_out_all_urbs(line6pcm);
+
+ if (err < 0) {
+ __sync_fetch_and_and(&line6pcm->flags, ~channels);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+int line6_pcm_stop(struct snd_line6_pcm *line6pcm, int channels)
+{
+ unsigned long flags_old = __sync_fetch_and_and(&line6pcm->flags, ~channels);
+ unsigned long flags_new = flags_old & ~channels;
+
+ if (((flags_old & MASK_CAPTURE) != 0) &&
+ ((flags_new & MASK_CAPTURE) == 0)) {
+ line6_unlink_audio_in_urbs(line6pcm);
+ kfree(line6pcm->buffer_in);
+ line6pcm->buffer_in = NULL;
+ }
+
+ if (((flags_old & MASK_PLAYBACK) != 0) &&
+ ((flags_new & MASK_PLAYBACK) == 0)) {
+ line6_unlink_audio_out_urbs(line6pcm);
+ kfree(line6pcm->buffer_out);
+ line6pcm->buffer_out = NULL;
+ }
+
+#if LINE6_BACKUP_MONITOR_SIGNAL
+ if (line6pcm->prev_fbuf != NULL)
+ kfree(line6pcm->prev_fbuf);
+#endif
+
+ return 0;
+}
+