All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/15] Line6 POD X3/X3Live suport
@ 2016-08-11 19:02 Andrej Krutak
  2016-08-11 19:02 ` [PATCH 01/15] ALSA: line6: Make driver configuration more generic Andrej Krutak
                   ` (18 more replies)
  0 siblings, 19 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-08-11 19:02 UTC (permalink / raw)
  To: alsa-devel; +Cc: Andrej Krutak

Hello all,


attached is the first version of the driver, based on POD HD. They share a lot
of similarities, but at the same time there were some generalizations needed. I
think POD HD could follow up on these patches to add hwdep support, perhaps
helix too... I tried to not introduce regressions to the old HW.

Anyhow, this is probably not the final version of the patches, as it is my
first submission to upstream - I'm sure there will be some issues.
Especially the patch
"Use device_create_file instead of snd_card_add_dev_attr" I have to revisit,
it may not be necessarry in the end.

Other than that, the patches mostly pass `make checkpatches`. I've been
testing the driver for a while now, including lock debugging options etc.,
and there don't seem to be functional problems.

There's one missing thing - the driver uses bulk USB interface of the
device, but so far I wasn't able to make the usb_driver_claim_interface()
work... I hope this can be added later, if someone (or I) finds time.


Thanks for your inputs, greetings,


Andrej Krutak (15):
  ALSA: line6: Make driver configuration more generic.
  ALSA: line6: Add LINE6_CAP_IN_NEEDS_OUT, a void playback stream during
    capture
  ALSA: line6: Distinguish device init (ctrl EP) and MIDI data transfer
    (int EP)
  ALSA: line6: Add support for POD X3
  ALSA: line6: Use device_create_file instead of snd_card_add_dev_attr
  ALSA: line6: Allow bulk endpoints instead of interrupt endpoints
  ALSA: line6: Allow processing of raw incoming messages
  ALSA: line6: Cleanup initialization
  ALSA: line6: Add hwdep interface to access the POD control messages
  ALSA: line6: Add proper locks for hwdep open/release/read
  ALSA: line6: Only free buffer if it is set.
  ALSA: line6: Give up on the lock while URBs are released.
  ALSA: line6: Add support for POD X3 Live (only USB ID differs from POD
    X3)
  ALSA: line6: Give up hwdep spinlock temporarily during read operation
  ALSA: line6: Remove double line6_pcm_release() after failed acquire.

 include/uapi/sound/asound.h |   3 +-
 sound/usb/line6/Kconfig     |   4 +-
 sound/usb/line6/capture.c   |  49 +++++--
 sound/usb/line6/driver.c    | 303 ++++++++++++++++++++++++++++++++++------
 sound/usb/line6/driver.h    |  71 +++++++---
 sound/usb/line6/midi.c      |   2 +-
 sound/usb/line6/pcm.c       |  83 +++++++----
 sound/usb/line6/pcm.h       |  19 +--
 sound/usb/line6/playback.c  |  37 +++--
 sound/usb/line6/pod.c       |  12 +-
 sound/usb/line6/podhd.c     | 328 +++++++++++++++++++++++++++++++++++++++++---
 sound/usb/line6/toneport.c  |   6 +-
 sound/usb/line6/variax.c    |   6 +-
 13 files changed, 767 insertions(+), 156 deletions(-)

-- 
1.9.1

^ permalink raw reply	[flat|nested] 81+ messages in thread

* [PATCH 01/15] ALSA: line6: Make driver configuration more generic.
  2016-08-11 19:02 [PATCH 00/15] Line6 POD X3/X3Live suport Andrej Krutak
@ 2016-08-11 19:02 ` Andrej Krutak
  2016-08-12  8:24   ` Takashi Iwai
  2016-08-11 19:02 ` [PATCH 02/15] ALSA: line6: Add LINE6_CAP_IN_NEEDS_OUT, a void playback stream during capture Andrej Krutak
                   ` (17 subsequent siblings)
  18 siblings, 1 reply; 81+ messages in thread
From: Andrej Krutak @ 2016-08-11 19:02 UTC (permalink / raw)
  To: alsa-devel; +Cc: Andrej Krutak

The main reasons are different settings for USB low/high speed and possible
different channel counts for in/out; required by POD X3.

This includes:
* iso_buffers (count of iso buffers depends on USB speed, 2 is not enough
  for high speed)
* bytes_per_frame -> bytes_per_channel
* max_packet_size -> max_packet_size_in/out
* USB_INTERVALS_PER_SECOND -> LOW/HIGH settings
  (high needs 8000, instead of 1000)

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/capture.c  | 44 ++++++++++++++++++++++++++++++--------------
 sound/usb/line6/driver.c   | 15 ++++++++++-----
 sound/usb/line6/driver.h   | 42 ++++++++++++++++++++++++++----------------
 sound/usb/line6/pcm.c      | 43 +++++++++++++++++++++++++------------------
 sound/usb/line6/pcm.h      | 15 +++++++--------
 sound/usb/line6/playback.c | 37 +++++++++++++++++++++++++------------
 sound/usb/line6/pod.c      |  3 +--
 sound/usb/line6/podhd.c    |  4 +---
 sound/usb/line6/toneport.c |  2 +-
 9 files changed, 126 insertions(+), 79 deletions(-)

diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c
index f518fbb..bacf03f 100644
--- a/sound/usb/line6/capture.c
+++ b/sound/usb/line6/capture.c
@@ -29,10 +29,10 @@ static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm)
 	int ret;
 	struct urb *urb_in;
 
-	index =
-	    find_first_zero_bit(&line6pcm->in.active_urbs, LINE6_ISO_BUFFERS);
+	index = find_first_zero_bit(
+		&line6pcm->in.active_urbs, line6pcm->line6->iso_buffers);
 
-	if (index < 0 || index >= LINE6_ISO_BUFFERS) {
+	if (index < 0 || index >= line6pcm->line6->iso_buffers) {
 		dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
 		return -EINVAL;
 	}
@@ -44,13 +44,13 @@ static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm)
 		struct usb_iso_packet_descriptor *fin =
 		    &urb_in->iso_frame_desc[i];
 		fin->offset = urb_size;
-		fin->length = line6pcm->max_packet_size;
-		urb_size += line6pcm->max_packet_size;
+		fin->length = line6pcm->max_packet_size_in;
+		urb_size += line6pcm->max_packet_size_in;
 	}
 
 	urb_in->transfer_buffer =
 	    line6pcm->in.buffer +
-	    index * LINE6_ISO_PACKETS * line6pcm->max_packet_size;
+	    index * LINE6_ISO_PACKETS * line6pcm->max_packet_size_in;
 	urb_in->transfer_buffer_length = urb_size;
 	urb_in->context = line6pcm;
 
@@ -73,7 +73,7 @@ int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm)
 {
 	int ret = 0, i;
 
-	for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
+	for (i = 0; i < line6pcm->line6->iso_buffers; ++i) {
 		ret = submit_audio_in_urb(line6pcm);
 		if (ret < 0)
 			break;
@@ -90,7 +90,9 @@ void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize)
 	struct snd_pcm_substream *substream =
 	    get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE);
 	struct snd_pcm_runtime *runtime = substream->runtime;
-	const int bytes_per_frame = line6pcm->properties->bytes_per_frame;
+	const int bytes_per_frame =
+		line6pcm->properties->bytes_per_channel *
+		line6pcm->properties->capture_hw.channels_max;
 	int frames = fsize / bytes_per_frame;
 
 	if (runtime == NULL)
@@ -154,7 +156,7 @@ static void audio_in_callback(struct urb *urb)
 	line6pcm->in.last_frame = urb->start_frame;
 
 	/* find index of URB */
-	for (index = 0; index < LINE6_ISO_BUFFERS; ++index)
+	for (index = 0; index < line6pcm->line6->iso_buffers; ++index)
 		if (urb == line6pcm->in.urbs[index])
 			break;
 
@@ -173,17 +175,26 @@ static void audio_in_callback(struct urb *urb)
 		fbuf = urb->transfer_buffer + fin->offset;
 		fsize = fin->actual_length;
 
-		if (fsize > line6pcm->max_packet_size) {
+		if (fsize > line6pcm->max_packet_size_in) {
 			dev_err(line6pcm->line6->ifcdev,
 				"driver and/or device bug: packet too large (%d > %d)\n",
-				fsize, line6pcm->max_packet_size);
+				fsize, line6pcm->max_packet_size_in);
 		}
 
 		length += fsize;
 
-		/* the following assumes LINE6_ISO_PACKETS == 1: */
+#if LINE6_ISO_PACKETS != 1
+# error "The following assumes LINE6_ISO_PACKETS == 1"
+/* TODO:
+   Also, if iso_buffers != 2, the prev frame is almost random at playback side.
+   This needs to be redesigned. It should be "stable", but we may experience
+   sync problems on such high-speed configs.
+*/
+#endif
 		line6pcm->prev_fbuf = fbuf;
-		line6pcm->prev_fsize = fsize;
+		line6pcm->prev_fsize = fsize /
+			(line6pcm->properties->bytes_per_channel *
+			line6pcm->properties->capture_hw.channels_max);
 
 		if (!test_bit(LINE6_STREAM_IMPULSE, &line6pcm->in.running) &&
 		    test_bit(LINE6_STREAM_PCM, &line6pcm->in.running) &&
@@ -247,8 +258,13 @@ int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm)
 	struct usb_line6 *line6 = line6pcm->line6;
 	int i;
 
+	line6pcm->in.urbs = kzalloc(
+		sizeof(struct urb *) * line6->iso_buffers, GFP_KERNEL);
+	if (line6pcm->in.urbs == NULL)
+		return -ENOMEM;
+
 	/* create audio URBs and fill in constant values: */
-	for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
+	for (i = 0; i < line6->iso_buffers; ++i) {
 		struct urb *urb;
 
 		/* URB for audio in: */
diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 81b7da8..efeb16a8 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -462,13 +462,17 @@ static void line6_destruct(struct snd_card *card)
 static void line6_get_interval(struct usb_line6 *line6)
 {
 	struct usb_device *usbdev = line6->usbdev;
-	struct usb_host_endpoint *ep;
-	unsigned pipe = usb_rcvintpipe(usbdev, line6->properties->ep_ctrl_r);
-	unsigned epnum = usb_pipeendpoint(pipe);
-
-	ep = usbdev->ep_in[epnum];
+	struct usb_host_endpoint *ep = usbdev->ep_in[line6->properties->ep_ctrl_r];
 	if (ep) {
 		line6->interval = ep->desc.bInterval;
+		if (usbdev->speed == USB_SPEED_LOW) {
+			line6->intervals_per_second = USB_LOW_INTERVALS_PER_SECOND;
+			line6->iso_buffers = USB_LOW_ISO_BUFFERS;
+		} else {
+			line6->intervals_per_second = USB_HIGH_INTERVALS_PER_SECOND;
+			line6->iso_buffers = USB_HIGH_ISO_BUFFERS;
+		}
+
 		line6->max_packet_size = le16_to_cpu(ep->desc.wMaxPacketSize);
 	} else {
 		dev_err(line6->ifcdev,
@@ -558,6 +562,7 @@ int line6_probe(struct usb_interface *interface,
 	/* query interface number */
 	interface_number = interface->cur_altsetting->desc.bInterfaceNumber;
 
+	/* TODO reserves the bus bandwidth even without actual transfer */
 	ret = usb_set_interface(usbdev, interface_number,
 				properties->altsetting);
 	if (ret < 0) {
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index 7da643e..24cd667 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -18,39 +18,45 @@
 
 #include "midi.h"
 
-#define USB_INTERVALS_PER_SECOND 1000
+/* USB 1.1 speed configuration */
+#define USB_LOW_INTERVALS_PER_SECOND (1000)
+#define USB_LOW_ISO_BUFFERS (2)
+
+/* USB 2.0+ speed configuration */
+#define USB_HIGH_INTERVALS_PER_SECOND (8000)
+#define USB_HIGH_ISO_BUFFERS (16)
 
 /* Fallback USB interval and max packet size values */
-#define LINE6_FALLBACK_INTERVAL 10
-#define LINE6_FALLBACK_MAXPACKETSIZE 16
+#define LINE6_FALLBACK_INTERVAL (10)
+#define LINE6_FALLBACK_MAXPACKETSIZE (16)
 
-#define LINE6_TIMEOUT 1
-#define LINE6_BUFSIZE_LISTEN 32
-#define LINE6_MESSAGE_MAXLEN 256
+#define LINE6_TIMEOUT (1)
+#define LINE6_BUFSIZE_LISTEN (64)
+#define LINE6_MESSAGE_MAXLEN (256)
 
 /*
 	Line 6 MIDI control commands
 */
-#define LINE6_PARAM_CHANGE   0xb0
-#define LINE6_PROGRAM_CHANGE 0xc0
-#define LINE6_SYSEX_BEGIN    0xf0
-#define LINE6_SYSEX_END      0xf7
-#define LINE6_RESET          0xff
+#define LINE6_PARAM_CHANGE   (0xb0)
+#define LINE6_PROGRAM_CHANGE (0xc0)
+#define LINE6_SYSEX_BEGIN    (0xf0)
+#define LINE6_SYSEX_END      (0xf7)
+#define LINE6_RESET          (0xff)
 
 /*
 	MIDI channel for messages initiated by the host
 	(and eventually echoed back by the device)
 */
-#define LINE6_CHANNEL_HOST   0x00
+#define LINE6_CHANNEL_HOST   (0x00)
 
 /*
 	MIDI channel for messages initiated by the device
 */
-#define LINE6_CHANNEL_DEVICE 0x02
+#define LINE6_CHANNEL_DEVICE (0x02)
 
-#define LINE6_CHANNEL_UNKNOWN 5	/* don't know yet what this is good for */
+#define LINE6_CHANNEL_UNKNOWN (5)	/* don't know yet what this is good for */
 
-#define LINE6_CHANNEL_MASK 0x0f
+#define LINE6_CHANNEL_MASK (0x0f)
 
 #define CHECK_STARTUP_PROGRESS(x, n)	\
 do {					\
@@ -109,8 +115,12 @@ struct usb_line6 {
 	/* Properties */
 	const struct line6_properties *properties;
 
-	/* Interval (ms) */
+	/* Interval (frames) */
 	int interval;
+	int intervals_per_second;
+
+	/* Number of isochronous URBs used for frame transfers */
+	int iso_buffers;
 
 	/* Maximum size of USB packet */
 	int max_packet_size;
diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c
index 204cc07..210c85f 100644
--- a/sound/usb/line6/pcm.c
+++ b/sound/usb/line6/pcm.c
@@ -105,7 +105,7 @@ static void line6_unlink_audio_urbs(struct snd_line6_pcm *line6pcm,
 {
 	int i;
 
-	for (i = 0; i < LINE6_ISO_BUFFERS; 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]);
@@ -125,7 +125,7 @@ static void line6_wait_clear_audio_urbs(struct snd_line6_pcm *line6pcm,
 
 	do {
 		alive = 0;
-		for (i = 0; i < LINE6_ISO_BUFFERS; i++) {
+		for (i = 0; i < line6pcm->line6->iso_buffers; i++) {
 			if (test_bit(i, &pcms->active_urbs))
 				alive++;
 		}
@@ -147,15 +147,20 @@ get_stream(struct snd_line6_pcm *line6pcm, int direction)
 }
 
 /* allocate a buffer if not opened yet;
- * call this in line6pcm.state_change mutex
+ * call this in line6pcm.state_mutex
  */
 static int line6_buffer_acquire(struct snd_line6_pcm *line6pcm,
-				struct line6_pcm_stream *pstr, int type)
+				struct line6_pcm_stream *pstr, int direction, int type)
 {
+	const int 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(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS *
-				       line6pcm->max_packet_size, GFP_KERNEL);
+		pstr->buffer = kmalloc(line6pcm->line6->iso_buffers *
+							   LINE6_ISO_PACKETS * pkt_size, GFP_KERNEL);
 		if (!pstr->buffer)
 			return -ENOMEM;
 	}
@@ -163,12 +168,11 @@ static int line6_buffer_acquire(struct snd_line6_pcm *line6pcm,
 }
 
 /* free a buffer if all streams are closed;
- * call this in line6pcm.state_change mutex
+ * call this in line6pcm.state_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);
@@ -195,6 +199,7 @@ static int line6_stream_start(struct snd_line6_pcm *line6pcm, int direction,
 		else
 			ret = line6_submit_audio_in_all_urbs(line6pcm);
 	}
+
 	if (ret < 0)
 		clear_bit(type, &pstr->running);
 	spin_unlock_irqrestore(&pstr->lock, flags);
@@ -433,24 +438,26 @@ static struct snd_kcontrol_new line6_controls[] = {
 /*
 	Cleanup the PCM device.
 */
-static void cleanup_urbs(struct line6_pcm_stream *pcms)
+static void cleanup_urbs(struct line6_pcm_stream *pcms, int iso_buffers)
 {
 	int i;
 
-	for (i = 0; i < LINE6_ISO_BUFFERS; i++) {
+	for (i = 0; i < iso_buffers; i++) {
 		if (pcms->urbs[i]) {
 			usb_kill_urb(pcms->urbs[i]);
 			usb_free_urb(pcms->urbs[i]);
 		}
 	}
+	kfree(pcms->urbs);
+	pcms->urbs = NULL;
 }
 
 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);
+	cleanup_urbs(&line6pcm->out, line6pcm->line6->iso_buffers);
+	cleanup_urbs(&line6pcm->in, line6pcm->line6->iso_buffers);
 	kfree(line6pcm);
 }
 
@@ -522,12 +529,12 @@ int line6_init_pcm(struct usb_line6 *line6,
 	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));
+	line6pcm->max_packet_size_in =
+		usb_maxpacket(line6->usbdev,
+			usb_rcvisocpipe(line6->usbdev, ep_read), 0);
+	line6pcm->max_packet_size_out =
+		usb_maxpacket(line6->usbdev,
+			usb_sndisocpipe(line6->usbdev, ep_write), 1);
 
 	spin_lock_init(&line6pcm->out.lock);
 	spin_lock_init(&line6pcm->in.lock);
diff --git a/sound/usb/line6/pcm.h b/sound/usb/line6/pcm.h
index 508410a..58d36f9 100644
--- a/sound/usb/line6/pcm.h
+++ b/sound/usb/line6/pcm.h
@@ -20,9 +20,6 @@
 
 #include "driver.h"
 
-/* number of URBs */
-#define LINE6_ISO_BUFFERS	2
-
 /*
 	number of USB frames per URB
 	The Line 6 Windows driver always transmits two frames per packet, but
@@ -31,7 +28,8 @@
 */
 #define LINE6_ISO_PACKETS	1
 
-/* in a "full speed" device (such as the PODxt Pro) this means 1ms */
+/* in a "full speed" device (such as the PODxt Pro) this means 1ms,
+   for "high speed" it's 1/8ms */
 #define LINE6_ISO_INTERVAL	1
 
 #define LINE6_IMPULSE_DEFAULT_PERIOD 100
@@ -85,12 +83,12 @@ enum {
 struct line6_pcm_properties {
 	struct snd_pcm_hardware playback_hw, capture_hw;
 	struct snd_pcm_hw_constraint_ratdens rates;
-	int bytes_per_frame;
+	int bytes_per_channel;
 };
 
 struct line6_pcm_stream {
 	/* allocated URBs */
-	struct urb *urbs[LINE6_ISO_BUFFERS];
+	struct urb **urbs;
 
 	/* Temporary buffer;
 	 * Since the packet size is not known in advance, this buffer is
@@ -157,11 +155,12 @@ struct snd_line6_pcm {
 	/* Previously captured frame (for software monitoring) */
 	unsigned char *prev_fbuf;
 
-	/* Size of previously captured frame (for software monitoring) */
+	/* Size of previously captured frame (for software monitoring/sync) */
 	int prev_fsize;
 
 	/* Maximum size of USB packet */
-	int max_packet_size;
+	int max_packet_size_in;
+	int max_packet_size_out;
 
 	/* PCM playback volume (left and right) */
 	int volume_playback[2];
diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c
index 97ed593..4833c51 100644
--- a/sound/usb/line6/playback.c
+++ b/sound/usb/line6/playback.c
@@ -146,18 +146,20 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
 	int index;
 	int i, urb_size, urb_frames;
 	int ret;
-	const int bytes_per_frame = line6pcm->properties->bytes_per_frame;
+	const int bytes_per_frame =
+		line6pcm->properties->bytes_per_channel *
+		line6pcm->properties->playback_hw.channels_max;
 	const int frame_increment =
 		line6pcm->properties->rates.rats[0].num_min;
 	const int frame_factor =
 		line6pcm->properties->rates.rats[0].den *
-		(USB_INTERVALS_PER_SECOND / LINE6_ISO_INTERVAL);
+		(line6pcm->line6->intervals_per_second / LINE6_ISO_INTERVAL);
 	struct urb *urb_out;
 
-	index =
-	    find_first_zero_bit(&line6pcm->out.active_urbs, LINE6_ISO_BUFFERS);
+	index = find_first_zero_bit(
+		&line6pcm->out.active_urbs, line6pcm->line6->iso_buffers);
 
-	if (index < 0 || index >= LINE6_ISO_BUFFERS) {
+	if (index < 0 || index >= line6pcm->line6->iso_buffers) {
 		dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
 		return -EINVAL;
 	}
@@ -165,6 +167,7 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
 	urb_out = line6pcm->out.urbs[index];
 	urb_size = 0;
 
+	/* TODO: this may not work for LINE6_ISO_PACKETS != 1 */
 	for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
 		/* compute frame size for given sampling rate */
 		int fsize = 0;
@@ -178,9 +181,11 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
 			line6pcm->out.count += frame_increment;
 			n = line6pcm->out.count / frame_factor;
 			line6pcm->out.count -= n * frame_factor;
-			fsize = n * bytes_per_frame;
+			fsize = n;
 		}
 
+		fsize *= bytes_per_frame;
+
 		fout->offset = urb_size;
 		fout->length = fsize;
 		urb_size += fsize;
@@ -195,7 +200,7 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
 	urb_frames = urb_size / bytes_per_frame;
 	urb_out->transfer_buffer =
 	    line6pcm->out.buffer +
-	    index * LINE6_ISO_PACKETS * line6pcm->max_packet_size;
+	    index * LINE6_ISO_PACKETS * line6pcm->max_packet_size_out;
 	urb_out->transfer_buffer_length = urb_size;
 	urb_out->context = line6pcm;
 
@@ -286,7 +291,7 @@ int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm)
 {
 	int ret = 0, i;
 
-	for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
+	for (i = 0; i < line6pcm->line6->iso_buffers; ++i) {
 		ret = submit_audio_out_urb(line6pcm);
 		if (ret < 0)
 			break;
@@ -305,6 +310,9 @@ static void audio_out_callback(struct urb *urb)
 	struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context;
 	struct snd_pcm_substream *substream =
 	    get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK);
+	const int bytes_per_frame =
+		line6pcm->properties->bytes_per_channel *
+		line6pcm->properties->playback_hw.channels_max;
 
 #if USE_CLEAR_BUFFER_WORKAROUND
 	memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);
@@ -313,11 +321,11 @@ static void audio_out_callback(struct urb *urb)
 	line6pcm->out.last_frame = urb->start_frame;
 
 	/* find index of URB */
-	for (index = 0; index < LINE6_ISO_BUFFERS; index++)
+	for (index = 0; index < line6pcm->line6->iso_buffers; index++)
 		if (urb == line6pcm->out.urbs[index])
 			break;
 
-	if (index >= LINE6_ISO_BUFFERS)
+	if (index >= line6pcm->line6->iso_buffers)
 		return;		/* URB has been unlinked asynchronously */
 
 	for (i = 0; i < LINE6_ISO_PACKETS; i++)
@@ -329,7 +337,7 @@ static void audio_out_callback(struct urb *urb)
 		struct snd_pcm_runtime *runtime = substream->runtime;
 
 		line6pcm->out.pos_done +=
-		    length / line6pcm->properties->bytes_per_frame;
+		    length / bytes_per_frame;
 
 		if (line6pcm->out.pos_done >= runtime->buffer_size)
 			line6pcm->out.pos_done -= runtime->buffer_size;
@@ -401,8 +409,13 @@ int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm)
 	struct usb_line6 *line6 = line6pcm->line6;
 	int i;
 
+	line6pcm->out.urbs = kzalloc(
+		sizeof(struct urb *) * line6->iso_buffers, GFP_KERNEL);
+	if (line6pcm->out.urbs == NULL)
+		return -ENOMEM;
+
 	/* create audio URBs and fill in constant values: */
-	for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
+	for (i = 0; i < line6->iso_buffers; ++i) {
 		struct urb *urb;
 
 		/* URB for audio out: */
diff --git a/sound/usb/line6/pod.c b/sound/usb/line6/pod.c
index daf81d1..31e5864 100644
--- a/sound/usb/line6/pod.c
+++ b/sound/usb/line6/pod.c
@@ -83,7 +83,6 @@ struct usb_line6_pod {
 };
 
 #define POD_SYSEX_CODE 3
-#define POD_BYTES_PER_FRAME 6	/* 24bit audio (stereo) */
 
 /* *INDENT-OFF* */
 
@@ -167,7 +166,7 @@ static struct line6_pcm_properties pod_pcm_properties = {
 	.rates = {
 			    .nrats = 1,
 			    .rats = &pod_ratden},
-	.bytes_per_frame = POD_BYTES_PER_FRAME
+	.bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */
 };
 
 static const char pod_version_header[] = {
diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c
index 63dcaef..4fc4789 100644
--- a/sound/usb/line6/podhd.c
+++ b/sound/usb/line6/podhd.c
@@ -25,8 +25,6 @@ enum {
 	LINE6_PODHD500_1,
 };
 
-#define PODHD_BYTES_PER_FRAME 6	/* 24bit audio (stereo) */
-
 static struct snd_ratden podhd_ratden = {
 	.num_min = 48000,
 	.num_max = 48000,
@@ -73,7 +71,7 @@ static struct line6_pcm_properties podhd_pcm_properties = {
 	.rates = {
 			    .nrats = 1,
 			    .rats = &podhd_ratden},
-	.bytes_per_frame = PODHD_BYTES_PER_FRAME
+	.bytes_per_channel = 3 /* 24bit audio (stereo) */
 };
 
 /*
diff --git a/sound/usb/line6/toneport.c b/sound/usb/line6/toneport.c
index 6d4c50c..da76e03 100644
--- a/sound/usb/line6/toneport.c
+++ b/sound/usb/line6/toneport.c
@@ -114,7 +114,7 @@ static struct line6_pcm_properties toneport_pcm_properties = {
 	.rates = {
 			    .nrats = 1,
 			    .rats = &toneport_ratden},
-	.bytes_per_frame = 4
+	.bytes_per_channel = 2
 };
 
 static const struct {
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH 02/15] ALSA: line6: Add LINE6_CAP_IN_NEEDS_OUT, a void playback stream during capture
  2016-08-11 19:02 [PATCH 00/15] Line6 POD X3/X3Live suport Andrej Krutak
  2016-08-11 19:02 ` [PATCH 01/15] ALSA: line6: Make driver configuration more generic Andrej Krutak
@ 2016-08-11 19:02 ` Andrej Krutak
  2016-08-11 19:02 ` [PATCH 03/15] ALSA: line6: Distinguish device init (ctrl EP) and MIDI data transfer (int EP) Andrej Krutak
                   ` (16 subsequent siblings)
  18 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-08-11 19:02 UTC (permalink / raw)
  To: alsa-devel; +Cc: Andrej Krutak

E.g. POD X3 seems to require playback data to be sent to it to generate
capture data. Otherwise the device stalls and doesn't send any more capture
data until it's reset.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/capture.c  |  5 +++++
 sound/usb/line6/driver.h   |  4 +++-
 sound/usb/line6/pcm.c      | 37 ++++++++++++++++++++++++++++---------
 sound/usb/line6/pcm.h      |  4 +++-
 sound/usb/line6/toneport.c |  4 ++--
 5 files changed, 41 insertions(+), 13 deletions(-)

diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c
index bacf03f..21a297e 100644
--- a/sound/usb/line6/capture.c
+++ b/sound/usb/line6/capture.c
@@ -231,6 +231,8 @@ static int snd_line6_capture_open(struct snd_pcm_substream *substream)
 	if (err < 0)
 		return err;
 
+	line6_pcm_acquire(line6pcm, LINE6_STREAM_CAPTURE_HELPER, false);
+
 	runtime->hw = line6pcm->properties->capture_hw;
 	return 0;
 }
@@ -238,6 +240,9 @@ static int snd_line6_capture_open(struct snd_pcm_substream *substream)
 /* 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;
 }
 
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index 24cd667..0f0c45a 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -100,8 +100,10 @@ enum {
 	LINE6_CAP_CONTROL =	1 << 0,
 	/* device supports PCM input/output via USB */
 	LINE6_CAP_PCM =		1 << 1,
-	/* device support hardware monitoring */
+	/* device supports hardware monitoring */
 	LINE6_CAP_HWMON =	1 << 2,
+	/* device requires output data when input is read */
+	LINE6_CAP_IN_NEEDS_OUT = 1 << 4,
 };
 
 /*
diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c
index 210c85f..0af6d4e 100644
--- a/sound/usb/line6/pcm.c
+++ b/sound/usb/line6/pcm.c
@@ -52,7 +52,7 @@ static int snd_line6_impulse_volume_put(struct snd_kcontrol *kcontrol,
 
 	line6pcm->impulse_volume = value;
 	if (value > 0) {
-		err = line6_pcm_acquire(line6pcm, LINE6_STREAM_IMPULSE);
+		err = line6_pcm_acquire(line6pcm, LINE6_STREAM_IMPULSE, true);
 		if (err < 0) {
 			line6pcm->impulse_volume = 0;
 			line6_pcm_release(line6pcm, LINE6_STREAM_IMPULSE);
@@ -241,6 +241,15 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
 		switch (cmd) {
 		case SNDRV_PCM_TRIGGER_START:
 		case SNDRV_PCM_TRIGGER_RESUME:
+			if ((s->stream == SNDRV_PCM_STREAM_CAPTURE) &&
+				(line6pcm->line6->properties->capabilities &
+					LINE6_CAP_IN_NEEDS_OUT)
+			) {
+				err = line6_stream_start(line6pcm, SNDRV_PCM_STREAM_PLAYBACK,
+						 LINE6_STREAM_CAPTURE_HELPER);
+				if (err < 0)
+					return err;
+			}
 			err = line6_stream_start(line6pcm, s->stream,
 						 LINE6_STREAM_PCM);
 			if (err < 0)
@@ -249,6 +258,13 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
 
 		case SNDRV_PCM_TRIGGER_STOP:
 		case SNDRV_PCM_TRIGGER_SUSPEND:
+			if ((s->stream == SNDRV_PCM_STREAM_CAPTURE) &&
+				(line6pcm->line6->properties->capabilities &
+					LINE6_CAP_IN_NEEDS_OUT)
+			) {
+				line6_stream_stop(line6pcm, SNDRV_PCM_STREAM_PLAYBACK,
+					  LINE6_STREAM_CAPTURE_HELPER);
+			}
 			line6_stream_stop(line6pcm, s->stream,
 					  LINE6_STREAM_PCM);
 			break;
@@ -282,27 +298,30 @@ snd_pcm_uframes_t snd_line6_pointer(struct snd_pcm_substream *substream)
 	return pstr->pos_done;
 }
 
-/* Acquire and start duplex streams:
+/* 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)
+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, type);
+		ret = line6_buffer_acquire(line6pcm, pstr, dir, 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;
+	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);
@@ -338,7 +357,7 @@ int snd_line6_hw_params(struct snd_pcm_substream *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);
+	ret = line6_buffer_acquire(line6pcm, pstr, substream->stream, LINE6_STREAM_PCM);
 	if (ret < 0)
 		goto error;
 
diff --git a/sound/usb/line6/pcm.h b/sound/usb/line6/pcm.h
index 58d36f9..5f796ef8 100644
--- a/sound/usb/line6/pcm.h
+++ b/sound/usb/line6/pcm.h
@@ -72,6 +72,7 @@ enum {
 	LINE6_STREAM_PCM,
 	LINE6_STREAM_MONITOR,
 	LINE6_STREAM_IMPULSE,
+	LINE6_STREAM_CAPTURE_HELPER,
 };
 
 /* misc bit flags for PCM operation */
@@ -190,7 +191,8 @@ extern int snd_line6_hw_params(struct snd_pcm_substream *substream,
 extern int snd_line6_hw_free(struct snd_pcm_substream *substream);
 extern snd_pcm_uframes_t snd_line6_pointer(struct snd_pcm_substream *substream);
 extern void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm);
-extern int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type);
+extern int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type,
+			       bool start);
 extern void line6_pcm_release(struct snd_line6_pcm *line6pcm, int type);
 
 #endif
diff --git a/sound/usb/line6/toneport.c b/sound/usb/line6/toneport.c
index da76e03..8e22f43 100644
--- a/sound/usb/line6/toneport.c
+++ b/sound/usb/line6/toneport.c
@@ -177,7 +177,7 @@ static int snd_toneport_monitor_put(struct snd_kcontrol *kcontrol,
 	line6pcm->volume_monitor = ucontrol->value.integer.value[0];
 
 	if (line6pcm->volume_monitor > 0) {
-		err = line6_pcm_acquire(line6pcm, LINE6_STREAM_MONITOR);
+		err = line6_pcm_acquire(line6pcm, LINE6_STREAM_MONITOR, true);
 		if (err < 0) {
 			line6pcm->volume_monitor = 0;
 			line6_pcm_release(line6pcm, LINE6_STREAM_MONITOR);
@@ -246,7 +246,7 @@ static void toneport_start_pcm(unsigned long arg)
 	struct usb_line6_toneport *toneport = (struct usb_line6_toneport *)arg;
 	struct usb_line6 *line6 = &toneport->line6;
 
-	line6_pcm_acquire(line6->line6pcm, LINE6_STREAM_MONITOR);
+	line6_pcm_acquire(line6->line6pcm, LINE6_STREAM_MONITOR, true);
 }
 
 /* control definition */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH 03/15] ALSA: line6: Distinguish device init (ctrl EP) and MIDI data transfer (int EP)
  2016-08-11 19:02 [PATCH 00/15] Line6 POD X3/X3Live suport Andrej Krutak
  2016-08-11 19:02 ` [PATCH 01/15] ALSA: line6: Make driver configuration more generic Andrej Krutak
  2016-08-11 19:02 ` [PATCH 02/15] ALSA: line6: Add LINE6_CAP_IN_NEEDS_OUT, a void playback stream during capture Andrej Krutak
@ 2016-08-11 19:02 ` Andrej Krutak
  2016-08-11 19:02 ` [PATCH 04/15] ALSA: line6: Add support for POD X3 Andrej Krutak
                   ` (15 subsequent siblings)
  18 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-08-11 19:02 UTC (permalink / raw)
  To: alsa-devel; +Cc: Andrej Krutak

POD X3 can initialize similarly to older PODs, but it doesn't have the MIDI
interface. Instead, configuration is done via proprietary bulk EP messages.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/driver.c | 10 +++++-----
 sound/usb/line6/driver.h |  6 ++++--
 sound/usb/line6/pod.c    |  9 ++++++++-
 sound/usb/line6/variax.c |  6 ++++--
 4 files changed, 21 insertions(+), 10 deletions(-)

diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index efeb16a8..5fd6cad 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -482,7 +482,7 @@ static void line6_get_interval(struct usb_line6 *line6)
 	}
 }
 
-static int line6_init_cap_control(struct usb_line6 *line6)
+static int line6_init_cap_control_midi(struct usb_line6 *line6)
 {
 	int ret;
 
@@ -572,8 +572,8 @@ int line6_probe(struct usb_interface *interface,
 
 	line6_get_interval(line6);
 
-	if (properties->capabilities & LINE6_CAP_CONTROL) {
-		ret = line6_init_cap_control(line6);
+	if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+		ret = line6_init_cap_control_midi(line6);
 		if (ret < 0)
 			goto error;
 	}
@@ -643,7 +643,7 @@ int line6_suspend(struct usb_interface *interface, pm_message_t message)
 
 	snd_power_change_state(line6->card, SNDRV_CTL_POWER_D3hot);
 
-	if (line6->properties->capabilities & LINE6_CAP_CONTROL)
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)
 		line6_stop_listen(line6);
 
 	if (line6pcm != NULL) {
@@ -662,7 +662,7 @@ int line6_resume(struct usb_interface *interface)
 {
 	struct usb_line6 *line6 = usb_get_intfdata(interface);
 
-	if (line6->properties->capabilities & LINE6_CAP_CONTROL)
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)
 		line6_start_listen(line6);
 
 	snd_power_change_state(line6->card, SNDRV_CTL_POWER_D0);
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index 0f0c45a..2551cb5 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -102,6 +102,8 @@ enum {
 	LINE6_CAP_PCM =		1 << 1,
 	/* device supports hardware monitoring */
 	LINE6_CAP_HWMON =	1 << 2,
+	/* device uses raw MIDI via USB (data endpoints) */
+	LINE6_CAP_CONTROL_MIDI = 1 << 3,
 	/* device requires output data when input is read */
 	LINE6_CAP_IN_NEEDS_OUT = 1 << 4,
 };
@@ -141,10 +143,10 @@ struct usb_line6 {
 	/* Line 6 MIDI device data structure */
 	struct snd_line6_midi *line6midi;
 
-	/* URB for listening to PODxt Pro control endpoint */
+	/* URB for listening to POD data endpoint */
 	struct urb *urb_listen;
 
-	/* Buffer for listening to PODxt Pro control endpoint */
+	/* Buffer for listening to POD data endpoint */
 	unsigned char *buffer_listen;
 
 	/* Buffer for message to be processed */
diff --git a/sound/usb/line6/pod.c b/sound/usb/line6/pod.c
index 31e5864..78642e7 100644
--- a/sound/usb/line6/pod.c
+++ b/sound/usb/line6/pod.c
@@ -475,6 +475,7 @@ static const struct line6_properties pod_properties_table[] = {
 		.id = "BassPODxt",
 		.name = "BassPODxt",
 		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI
 				| LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 5,
@@ -487,6 +488,7 @@ static const struct line6_properties pod_properties_table[] = {
 		.id = "BassPODxtLive",
 		.name = "BassPODxt Live",
 		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI
 				| LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 1,
@@ -499,6 +501,7 @@ static const struct line6_properties pod_properties_table[] = {
 		.id = "BassPODxtPro",
 		.name = "BassPODxt Pro",
 		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI
 				| LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 5,
@@ -510,7 +513,8 @@ static const struct line6_properties pod_properties_table[] = {
 	[LINE6_POCKETPOD] = {
 		.id = "PocketPOD",
 		.name = "Pocket POD",
-		.capabilities	= LINE6_CAP_CONTROL,
+		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI,
 		.altsetting = 0,
 		.ep_ctrl_r = 0x82,
 		.ep_ctrl_w = 0x02,
@@ -520,6 +524,7 @@ static const struct line6_properties pod_properties_table[] = {
 		.id = "PODxt",
 		.name = "PODxt",
 		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI
 				| LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 5,
@@ -532,6 +537,7 @@ static const struct line6_properties pod_properties_table[] = {
 		.id = "PODxtLive",
 		.name = "PODxt Live",
 		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI
 				| LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 1,
@@ -544,6 +550,7 @@ static const struct line6_properties pod_properties_table[] = {
 		.id = "PODxtPro",
 		.name = "PODxt Pro",
 		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI
 				| LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 5,
diff --git a/sound/usb/line6/variax.c b/sound/usb/line6/variax.c
index ddc23dd..0c4512d 100644
--- a/sound/usb/line6/variax.c
+++ b/sound/usb/line6/variax.c
@@ -259,7 +259,8 @@ static const struct line6_properties variax_properties_table[] = {
 	[LINE6_PODXTLIVE_VARIAX] = {
 		.id = "PODxtLive",
 		.name = "PODxt Live",
-		.capabilities	= LINE6_CAP_CONTROL,
+		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI,
 		.altsetting = 1,
 		.ep_ctrl_r = 0x86,
 		.ep_ctrl_w = 0x05,
@@ -269,7 +270,8 @@ static const struct line6_properties variax_properties_table[] = {
 	[LINE6_VARIAX] = {
 		.id = "Variax",
 		.name = "Variax Workbench",
-		.capabilities	= LINE6_CAP_CONTROL,
+		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI,
 		.altsetting = 1,
 		.ep_ctrl_r = 0x82,
 		.ep_ctrl_w = 0x01,
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH 04/15] ALSA: line6: Add support for POD X3
  2016-08-11 19:02 [PATCH 00/15] Line6 POD X3/X3Live suport Andrej Krutak
                   ` (2 preceding siblings ...)
  2016-08-11 19:02 ` [PATCH 03/15] ALSA: line6: Distinguish device init (ctrl EP) and MIDI data transfer (int EP) Andrej Krutak
@ 2016-08-11 19:02 ` Andrej Krutak
  2016-08-11 19:02 ` [PATCH 05/15] ALSA: line6: Use device_create_file instead of snd_card_add_dev_attr Andrej Krutak
                   ` (14 subsequent siblings)
  18 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-08-11 19:02 UTC (permalink / raw)
  To: alsa-devel; +Cc: Andrej Krutak

This includes audio in/out and basic initialization via control EP (emulates
what original driver does). The initialization is done similarly to original
POD, firmware and serial IDs are read and exported via sysfs.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/Kconfig |   4 +-
 sound/usb/line6/podhd.c | 276 +++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 265 insertions(+), 15 deletions(-)

diff --git a/sound/usb/line6/Kconfig b/sound/usb/line6/Kconfig
index f4585d37..8ffcf48 100644
--- a/sound/usb/line6/Kconfig
+++ b/sound/usb/line6/Kconfig
@@ -21,10 +21,10 @@ config SND_USB_POD
 	      re-amping)
 
 config SND_USB_PODHD
-	tristate "Line 6 POD HD300/400/500 USB support"
+	tristate "Line 6 POD X3/HD300/400/500 USB support"
 	select SND_USB_LINE6
 	help
-	  This is a driver for POD HD300, 400 and 500 devices.
+	  This is a driver for POD X3, HD300, 400 and 500 devices.
 
 config SND_USB_TONEPORT
 	tristate "TonePort GX, UX1 and UX2 USB support"
diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c
index 4fc4789..42fa376 100644
--- a/sound/usb/line6/podhd.c
+++ b/sound/usb/line6/podhd.c
@@ -2,6 +2,7 @@
  * Line 6 Pod HD
  *
  * Copyright (C) 2011 Stefan Hajnoczi <stefanha@gmail.com>
+ * Copyright (C) 2015 Andrej Krutak <dev@andree.sk>
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License as
@@ -18,11 +19,44 @@
 #include "driver.h"
 #include "pcm.h"
 
+#define PODHD_STARTUP_DELAY 500
+
+/*
+ * Stages of POD startup procedure
+ */
+enum {
+	PODHD_STARTUP_INIT = 1,
+	PODHD_STARTUP_SCHEDULE_WORKQUEUE,
+	PODHD_STARTUP_SETUP,
+	PODHD_STARTUP_LAST = PODHD_STARTUP_SETUP - 1
+};
+
 enum {
 	LINE6_PODHD300,
 	LINE6_PODHD400,
 	LINE6_PODHD500_0,
 	LINE6_PODHD500_1,
+	LINE6_PODX3,
+};
+
+struct usb_line6_podhd {
+	/* Generic Line 6 USB data */
+	struct usb_line6 line6;
+
+	/* Timer for device initialization */
+	struct timer_list startup_timer;
+
+	/* Work handler for device initialization */
+	struct work_struct startup_work;
+
+	/* Current progress in startup procedure */
+	int startup_progress;
+
+	/* Serial number of device */
+	u32 serial_number;
+
+	/* Firmware version */
+	int firmware_version;
 };
 
 static struct snd_ratden podhd_ratden = {
@@ -71,9 +105,196 @@ static struct line6_pcm_properties podhd_pcm_properties = {
 	.rates = {
 			    .nrats = 1,
 			    .rats = &podhd_ratden},
-	.bytes_per_channel = 3 /* 24bit audio (stereo) */
+	.bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */
 };
 
+static struct line6_pcm_properties podx3_pcm_properties = {
+	.playback_hw = {
+				  .info = (SNDRV_PCM_INFO_MMAP |
+					   SNDRV_PCM_INFO_INTERLEAVED |
+					   SNDRV_PCM_INFO_BLOCK_TRANSFER |
+					   SNDRV_PCM_INFO_MMAP_VALID |
+					   SNDRV_PCM_INFO_PAUSE |
+					   SNDRV_PCM_INFO_SYNC_START),
+				  .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+				  .rates = SNDRV_PCM_RATE_48000,
+				  .rate_min = 48000,
+				  .rate_max = 48000,
+				  .channels_min = 2,
+				  .channels_max = 2,
+				  .buffer_bytes_max = 60000,
+				  .period_bytes_min = 64,
+				  .period_bytes_max = 8192,
+				  .periods_min = 1,
+				  .periods_max = 1024},
+	.capture_hw = {
+				 .info = (SNDRV_PCM_INFO_MMAP |
+					  SNDRV_PCM_INFO_INTERLEAVED |
+					  SNDRV_PCM_INFO_BLOCK_TRANSFER |
+					  SNDRV_PCM_INFO_MMAP_VALID |
+					  SNDRV_PCM_INFO_SYNC_START),
+				 .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+				 .rates = SNDRV_PCM_RATE_48000,
+				 .rate_min = 48000,
+				 .rate_max = 48000,
+				 /* 1+2: Main signal (out), 3+4: Tone 1,
+				  * 5+6: Tone 2, 7+8: raw */
+				 .channels_min = 8,
+				 .channels_max = 8,
+				 .buffer_bytes_max = 60000,
+				 .period_bytes_min = 64,
+				 .period_bytes_max = 8192,
+				 .periods_min = 1,
+				 .periods_max = 1024},
+	.rates = {
+			    .nrats = 1,
+			    .rats = &podhd_ratden},
+	.bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */
+};
+
+static void podhd_startup_start_workqueue(unsigned long data);
+static void podhd_startup_workqueue(struct work_struct *work);
+static int podhd_startup_finalize(struct usb_line6_podhd *pod);
+
+static ssize_t serial_number_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct usb_interface *interface = to_usb_interface(dev);
+	struct usb_line6_podhd *pod = usb_get_intfdata(interface);
+
+	return sprintf(buf, "%u\n", pod->serial_number);
+}
+
+static ssize_t firmware_version_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct usb_interface *interface = to_usb_interface(dev);
+	struct usb_line6_podhd *pod = usb_get_intfdata(interface);
+
+	return sprintf(buf, "%06x\n", pod->firmware_version);
+}
+
+static DEVICE_ATTR_RO(firmware_version);
+static DEVICE_ATTR_RO(serial_number);
+
+static struct attribute *podhd_dev_attrs[] = {
+	&dev_attr_firmware_version.attr,
+	&dev_attr_serial_number.attr,
+	NULL
+};
+
+static const struct attribute_group podhd_dev_attr_group = {
+	.name = "podhd",
+	.attrs = podhd_dev_attrs,
+};
+
+/*
+ * POD X3 startup procedure.
+ *
+ * May be compatible with other POD HD's, since it's also similar to the
+ * previous POD setup. In any case, it doesn't seem to be required for the
+ * audio nor bulk interfaces to work.
+ */
+
+static void podhd_startup(struct usb_line6_podhd *pod)
+{
+	CHECK_STARTUP_PROGRESS(pod->startup_progress, PODHD_STARTUP_INIT);
+
+	/* delay startup procedure: */
+	line6_start_timer(&pod->startup_timer, PODHD_STARTUP_DELAY,
+		podhd_startup_start_workqueue, (unsigned long)pod);
+}
+
+static void podhd_startup_start_workqueue(unsigned long data)
+{
+	struct usb_line6_podhd *pod = (struct usb_line6_podhd *)data;
+
+	CHECK_STARTUP_PROGRESS(pod->startup_progress,
+		PODHD_STARTUP_SCHEDULE_WORKQUEUE);
+
+	/* schedule work for global work queue: */
+	schedule_work(&pod->startup_work);
+}
+
+static int podhd_dev_start(struct usb_line6_podhd *pod)
+{
+	int ret;
+	u8 init_bytes[8];
+	int i;
+	struct usb_device *usbdev = pod->line6.usbdev;
+
+	ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
+					0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+					0x11, 0,
+					NULL, 0, LINE6_TIMEOUT * HZ);
+	if (ret < 0) {
+		dev_err(pod->line6.ifcdev, "read request failed (error %d)\n", ret);
+		return ret;
+	}
+
+	/* NOTE: looks like some kind of ping message */
+	ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67,
+					USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+					0x11, 0x0,
+					&init_bytes, 3, LINE6_TIMEOUT * HZ);
+	if (ret < 0) {
+		dev_err(pod->line6.ifcdev,
+			"receive length failed (error %d)\n", ret);
+		return ret;
+	}
+
+	pod->firmware_version =
+		(init_bytes[0] << 16) | (init_bytes[1] << 8) | (init_bytes[2] << 0);
+
+	for (i = 0; i <= 16; i++) {
+		ret = line6_read_data(&pod->line6, 0xf000 + 0x08 * i, init_bytes, 8);
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
+					USB_REQ_SET_FEATURE,
+					USB_TYPE_STANDARD | USB_RECIP_DEVICE | USB_DIR_OUT,
+					1, 0,
+					NULL, 0, LINE6_TIMEOUT * HZ);
+	if (ret < 0) {
+		return ret;
+	}
+
+	return 0;
+}
+
+static void podhd_startup_workqueue(struct work_struct *work)
+{
+	struct usb_line6_podhd *pod =
+	    container_of(work, struct usb_line6_podhd, startup_work);
+
+	CHECK_STARTUP_PROGRESS(pod->startup_progress, PODHD_STARTUP_SETUP);
+
+	podhd_dev_start(pod);
+	line6_read_serial_number(&pod->line6, &pod->serial_number);
+
+	podhd_startup_finalize(pod);
+}
+
+static int podhd_startup_finalize(struct usb_line6_podhd *pod)
+{
+	struct usb_line6 *line6 = &pod->line6;
+
+	/* ALSA audio interface: */
+	return snd_card_register(line6->card);
+}
+
+static void podhd_disconnect(struct usb_line6 *line6)
+{
+	struct usb_line6_podhd *pod = (struct usb_line6_podhd *)line6;
+
+	if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) {
+		del_timer_sync(&pod->startup_timer);
+		cancel_work_sync(&pod->startup_work);
+	}
+}
+
 /*
 	Try to init POD HD device.
 */
@@ -81,6 +302,16 @@ static int podhd_init(struct usb_line6 *line6,
 		      const struct usb_device_id *id)
 {
 	int err;
+	struct usb_line6_podhd *pod = (struct usb_line6_podhd *) line6;
+
+	line6->disconnect = podhd_disconnect;
+
+	if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) {
+		/* create sysfs entries: */
+		err = snd_card_add_dev_attr(line6->card, &podhd_dev_attr_group);
+		if (err < 0)
+			return err;
+	}
 
 	/* initialize MIDI subsystem: */
 	err = line6_init_midi(line6);
@@ -88,12 +319,22 @@ static int podhd_init(struct usb_line6 *line6,
 		return err;
 
 	/* initialize PCM subsystem: */
-	err = line6_init_pcm(line6, &podhd_pcm_properties);
+	err = line6_init_pcm(line6,
+		(id->driver_info == LINE6_PODX3) ? &podx3_pcm_properties :
+		&podhd_pcm_properties);
 	if (err < 0)
 		return err;
 
-	/* register USB audio system: */
-	return snd_card_register(line6->card);
+	if (!(pod->line6.properties->capabilities & LINE6_CAP_CONTROL)) {
+		/* register USB audio system directly */
+		return podhd_startup_finalize(pod);
+	}
+
+	/* init device and delay registering */
+	init_timer(&pod->startup_timer);
+	INIT_WORK(&pod->startup_work, podhd_startup_workqueue);
+	podhd_startup(pod);
+	return 0;
 }
 
 #define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod)
@@ -101,10 +342,12 @@ static int podhd_init(struct usb_line6 *line6,
 
 /* table of devices that work with this driver */
 static const struct usb_device_id podhd_id_table[] = {
+	/* TODO: no need to alloc data interfaces when only audio is used */
 	{ LINE6_DEVICE(0x5057),    .driver_info = LINE6_PODHD300 },
 	{ LINE6_DEVICE(0x5058),    .driver_info = LINE6_PODHD400 },
 	{ LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500_0 },
 	{ LINE6_IF_NUM(0x414D, 1), .driver_info = LINE6_PODHD500_1 },
+	{ LINE6_IF_NUM(0x414A, 0), .driver_info = LINE6_PODX3 },
 	{}
 };
 
@@ -114,8 +357,7 @@ static const struct line6_properties podhd_properties_table[] = {
 	[LINE6_PODHD300] = {
 		.id = "PODHD300",
 		.name = "POD HD300",
-		.capabilities	= LINE6_CAP_CONTROL
-				| LINE6_CAP_PCM
+		.capabilities	= LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 5,
 		.ep_ctrl_r = 0x84,
@@ -126,8 +368,7 @@ static const struct line6_properties podhd_properties_table[] = {
 	[LINE6_PODHD400] = {
 		.id = "PODHD400",
 		.name = "POD HD400",
-		.capabilities	= LINE6_CAP_CONTROL
-				| LINE6_CAP_PCM
+		.capabilities	= LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 5,
 		.ep_ctrl_r = 0x84,
@@ -138,8 +379,7 @@ static const struct line6_properties podhd_properties_table[] = {
 	[LINE6_PODHD500_0] = {
 		.id = "PODHD500",
 		.name = "POD HD500",
-		.capabilities	= LINE6_CAP_CONTROL
-				| LINE6_CAP_PCM
+		.capabilities	= LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 1,
 		.ep_ctrl_r = 0x81,
@@ -150,8 +390,7 @@ static const struct line6_properties podhd_properties_table[] = {
 	[LINE6_PODHD500_1] = {
 		.id = "PODHD500",
 		.name = "POD HD500",
-		.capabilities	= LINE6_CAP_CONTROL
-				| LINE6_CAP_PCM
+		.capabilities	= LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 1,
 		.ep_ctrl_r = 0x81,
@@ -159,6 +398,17 @@ static const struct line6_properties podhd_properties_table[] = {
 		.ep_audio_r = 0x86,
 		.ep_audio_w = 0x02,
 	},
+	[LINE6_PODX3] = {
+		.id = "PODX3",
+		.name = "POD X3",
+		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_PCM | LINE6_CAP_HWMON | LINE6_CAP_IN_NEEDS_OUT,
+		.altsetting = 1,
+		.ep_ctrl_r = 0x81,
+		.ep_ctrl_w = 0x01,
+		.ep_audio_r = 0x86,
+		.ep_audio_w = 0x02,
+	},
 };
 
 /*
@@ -169,7 +419,7 @@ static int podhd_probe(struct usb_interface *interface,
 {
 	return line6_probe(interface, id, "Line6-PODHD",
 			   &podhd_properties_table[id->driver_info],
-			   podhd_init, sizeof(struct usb_line6));
+			   podhd_init, sizeof(struct usb_line6_podhd));
 }
 
 static struct usb_driver podhd_driver = {
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH 05/15] ALSA: line6: Use device_create_file instead of snd_card_add_dev_attr
  2016-08-11 19:02 [PATCH 00/15] Line6 POD X3/X3Live suport Andrej Krutak
                   ` (3 preceding siblings ...)
  2016-08-11 19:02 ` [PATCH 04/15] ALSA: line6: Add support for POD X3 Andrej Krutak
@ 2016-08-11 19:02 ` Andrej Krutak
  2016-08-12  8:26   ` Takashi Iwai
  2016-08-11 19:02 ` [PATCH 06/15] ALSA: line6: Allow bulk endpoints instead of interrupt endpoints Andrej Krutak
                   ` (13 subsequent siblings)
  18 siblings, 1 reply; 81+ messages in thread
From: Andrej Krutak @ 2016-08-11 19:02 UTC (permalink / raw)
  To: alsa-devel; +Cc: Andrej Krutak

The latter seems to create invalid configuration, the device pointer passed
to the attr callbacks is not correct afterwards (results into segfaults when
accessing the attributes).

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/podhd.c | 60 ++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 44 insertions(+), 16 deletions(-)

diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c
index 42fa376..f72803a 100644
--- a/sound/usb/line6/podhd.c
+++ b/sound/usb/line6/podhd.c
@@ -57,6 +57,9 @@ struct usb_line6_podhd {
 
 	/* Firmware version */
 	int firmware_version;
+
+	/* Flag whether the sysfs files were created */
+	int devfiles_created;
 };
 
 static struct snd_ratden podhd_ratden = {
@@ -177,16 +180,37 @@ static ssize_t firmware_version_show(struct device *dev,
 static DEVICE_ATTR_RO(firmware_version);
 static DEVICE_ATTR_RO(serial_number);
 
-static struct attribute *podhd_dev_attrs[] = {
-	&dev_attr_firmware_version.attr,
-	&dev_attr_serial_number.attr,
-	NULL
-};
+/*
+ *       Create sysfs entries.
+ */
+static int podhd_create_attrs(struct device *dev)
+{
+	int err;
+	struct usb_interface *interface = to_usb_interface(dev);
+	struct usb_line6_podhd *pod = usb_get_intfdata(interface);
 
-static const struct attribute_group podhd_dev_attr_group = {
-	.name = "podhd",
-	.attrs = podhd_dev_attrs,
-};
+	err = device_create_file(dev, &dev_attr_firmware_version);
+	if (err < 0)
+		return err;
+	err = device_create_file(dev, &dev_attr_serial_number);
+	if (err < 0)
+		return err;
+
+	pod->devfiles_created = 1;
+	return 0;
+}
+
+static void podhd_remove_attrs(struct device *dev)
+{
+	struct usb_interface *interface = to_usb_interface(dev);
+	struct usb_line6_podhd *pod = usb_get_intfdata(interface);
+
+	if (pod->devfiles_created) {
+		device_remove_file(dev, &dev_attr_firmware_version);
+		device_remove_file(dev, &dev_attr_serial_number);
+		pod->devfiles_created = 0;
+	}
+}
 
 /*
  * POD X3 startup procedure.
@@ -281,6 +305,14 @@ static int podhd_startup_finalize(struct usb_line6_podhd *pod)
 {
 	struct usb_line6 *line6 = &pod->line6;
 
+	if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) {
+		/* create sysfs entries: */
+		int err = podhd_create_attrs(line6->ifcdev);
+
+		if (err < 0)
+			return err;
+	}
+
 	/* ALSA audio interface: */
 	return snd_card_register(line6->card);
 }
@@ -288,8 +320,11 @@ static int podhd_startup_finalize(struct usb_line6_podhd *pod)
 static void podhd_disconnect(struct usb_line6 *line6)
 {
 	struct usb_line6_podhd *pod = (struct usb_line6_podhd *)line6;
+	struct device *dev = line6->ifcdev;
 
 	if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) {
+		podhd_remove_attrs(dev);
+
 		del_timer_sync(&pod->startup_timer);
 		cancel_work_sync(&pod->startup_work);
 	}
@@ -306,13 +341,6 @@ static int podhd_init(struct usb_line6 *line6,
 
 	line6->disconnect = podhd_disconnect;
 
-	if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) {
-		/* create sysfs entries: */
-		err = snd_card_add_dev_attr(line6->card, &podhd_dev_attr_group);
-		if (err < 0)
-			return err;
-	}
-
 	/* initialize MIDI subsystem: */
 	err = line6_init_midi(line6);
 	if (err < 0)
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH 06/15] ALSA: line6: Allow bulk endpoints instead of interrupt endpoints
  2016-08-11 19:02 [PATCH 00/15] Line6 POD X3/X3Live suport Andrej Krutak
                   ` (4 preceding siblings ...)
  2016-08-11 19:02 ` [PATCH 05/15] ALSA: line6: Use device_create_file instead of snd_card_add_dev_attr Andrej Krutak
@ 2016-08-11 19:02 ` Andrej Krutak
  2016-08-11 19:02 ` [PATCH 07/15] ALSA: line6: Allow processing of raw incoming messages Andrej Krutak
                   ` (12 subsequent siblings)
  18 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-08-11 19:02 UTC (permalink / raw)
  To: alsa-devel; +Cc: Andrej Krutak

Some PODs (e.g. POD X3) have bulk instead of interrupt endpoints for
data transfer.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/driver.c | 63 ++++++++++++++++++++++++++++++++++++------------
 1 file changed, 48 insertions(+), 15 deletions(-)

diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 5fd6cad..9b16777 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -66,10 +66,17 @@ static int line6_start_listen(struct usb_line6 *line6)
 {
 	int err;
 
-	usb_fill_int_urb(line6->urb_listen, line6->usbdev,
-		usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r),
-		line6->buffer_listen, LINE6_BUFSIZE_LISTEN,
-		line6_data_received, line6, line6->interval);
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+		usb_fill_int_urb(line6->urb_listen, line6->usbdev,
+			usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r),
+			line6->buffer_listen, LINE6_BUFSIZE_LISTEN,
+			line6_data_received, line6, line6->interval);
+	} else {
+		usb_fill_bulk_urb(line6->urb_listen, line6->usbdev,
+			usb_rcvbulkpipe(line6->usbdev, line6->properties->ep_ctrl_r),
+			line6->buffer_listen, LINE6_BUFSIZE_LISTEN,
+			line6_data_received, line6);
+	}
 	line6->urb_listen->actual_length = 0;
 	err = usb_submit_urb(line6->urb_listen, GFP_ATOMIC);
 	return err;
@@ -90,6 +97,7 @@ static int line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
 				  int size)
 {
 	int i, done = 0;
+	const struct line6_properties *properties = line6->properties;
 
 	for (i = 0; i < size; i += line6->max_packet_size) {
 		int partial;
@@ -97,15 +105,21 @@ static int line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
 		int frag_size = min(line6->max_packet_size, size - i);
 		int retval;
 
-		retval = usb_interrupt_msg(line6->usbdev,
-					usb_sndintpipe(line6->usbdev,
-						line6->properties->ep_ctrl_w),
-					(char *)frag_buf, frag_size,
-					&partial, LINE6_TIMEOUT * HZ);
+		if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+			retval = usb_interrupt_msg(line6->usbdev,
+						usb_sndintpipe(line6->usbdev, properties->ep_ctrl_w),
+						(char *)frag_buf, frag_size,
+						&partial, LINE6_TIMEOUT * HZ);
+		} else {
+			retval = usb_bulk_msg(line6->usbdev,
+						usb_sndbulkpipe(line6->usbdev, properties->ep_ctrl_w),
+						(char *)frag_buf, frag_size,
+						&partial, LINE6_TIMEOUT * HZ);
+		}
 
 		if (retval) {
 			dev_err(line6->ifcdev,
-				"usb_interrupt_msg failed (%d)\n", retval);
+				"usb_bulk_msg failed (%d)\n", retval);
 			break;
 		}
 
@@ -140,10 +154,17 @@ static int line6_send_raw_message_async_part(struct message *msg,
 	int done = msg->done;
 	int bytes = min(msg->size - done, line6->max_packet_size);
 
-	usb_fill_int_urb(urb, line6->usbdev,
-		usb_sndintpipe(line6->usbdev, line6->properties->ep_ctrl_w),
-		(char *)msg->buffer + done, bytes,
-		line6_async_request_sent, msg, line6->interval);
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+		usb_fill_int_urb(urb, line6->usbdev,
+			usb_sndintpipe(line6->usbdev, line6->properties->ep_ctrl_w),
+			(char *)msg->buffer + done, bytes,
+			line6_async_request_sent, msg, line6->interval);
+	} else {
+		usb_fill_bulk_urb(urb, line6->usbdev,
+			usb_sndbulkpipe(line6->usbdev, line6->properties->ep_ctrl_w),
+			(char *)msg->buffer + done, bytes,
+			line6_async_request_sent, msg);
+	}
 
 	msg->done += bytes;
 	retval = usb_submit_urb(urb, GFP_ATOMIC);
@@ -462,7 +483,19 @@ static void line6_destruct(struct snd_card *card)
 static void line6_get_interval(struct usb_line6 *line6)
 {
 	struct usb_device *usbdev = line6->usbdev;
-	struct usb_host_endpoint *ep = usbdev->ep_in[line6->properties->ep_ctrl_r];
+	const struct line6_properties *properties = line6->properties;
+	int pipe;
+	struct usb_host_endpoint *ep;
+
+	if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+		pipe =
+			usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r);
+	} else {
+		pipe =
+			usb_rcvbulkpipe(line6->usbdev, line6->properties->ep_ctrl_r);
+	}
+	ep = usbdev->ep_in[usb_pipeendpoint(pipe)];
+
 	if (ep) {
 		line6->interval = ep->desc.bInterval;
 		if (usbdev->speed == USB_SPEED_LOW) {
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH 07/15] ALSA: line6: Allow processing of raw incoming messages
  2016-08-11 19:02 [PATCH 00/15] Line6 POD X3/X3Live suport Andrej Krutak
                   ` (5 preceding siblings ...)
  2016-08-11 19:02 ` [PATCH 06/15] ALSA: line6: Allow bulk endpoints instead of interrupt endpoints Andrej Krutak
@ 2016-08-11 19:02 ` Andrej Krutak
  2016-08-11 19:02 ` [PATCH 08/15] ALSA: line6: Cleanup initialization Andrej Krutak
                   ` (11 subsequent siblings)
  18 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-08-11 19:02 UTC (permalink / raw)
  To: alsa-devel; +Cc: Andrej Krutak

Not all PODs use MIDI via USB data interface, thus allow avoiding
that code and instead using direct processing.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/driver.c | 59 ++++++++++++++++++++++++++++--------------------
 sound/usb/line6/driver.h |  8 ++++---
 sound/usb/line6/midi.c   |  2 +-
 3 files changed, 40 insertions(+), 29 deletions(-)

diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 9b16777..853a143 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -290,26 +290,31 @@ static void line6_data_received(struct urb *urb)
 	if (urb->status == -ESHUTDOWN)
 		return;
 
-	done =
-	    line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length);
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+		done =
+			line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length);
 
-	if (done < urb->actual_length) {
-		line6_midibuf_ignore(mb, done);
-		dev_dbg(line6->ifcdev, "%d %d buffer overflow - message skipped\n",
-			done, urb->actual_length);
-	}
+		if (done < urb->actual_length) {
+			line6_midibuf_ignore(mb, done);
+			dev_dbg(line6->ifcdev, "%d %d buffer overflow - message skipped\n",
+				done, urb->actual_length);
+		}
 
-	for (;;) {
-		done =
-		    line6_midibuf_read(mb, line6->buffer_message,
-				       LINE6_MESSAGE_MAXLEN);
+		for (;;) {
+			done =
+				line6_midibuf_read(mb, line6->buffer_message,
+						LINE6_MESSAGE_MAXLEN);
 
-		if (done == 0)
-			break;
+			if (done == 0)
+				break;
 
-		line6->message_length = done;
-		line6_midi_receive(line6, line6->buffer_message, done);
+			line6->message_length = done;
+			line6_midi_receive(line6, line6->buffer_message, done);
 
+			if (line6->process_message)
+				line6->process_message(line6);
+		}
+	} else {
 		if (line6->process_message)
 			line6->process_message(line6);
 	}
@@ -469,7 +474,9 @@ static void line6_destruct(struct snd_card *card)
 	struct usb_device *usbdev = line6->usbdev;
 
 	/* free buffer memory first: */
-	kfree(line6->buffer_message);
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)
+		kfree(line6->buffer_message);
+
 	kfree(line6->buffer_listen);
 
 	/* then free URBs: */
@@ -515,7 +522,7 @@ static void line6_get_interval(struct usb_line6 *line6)
 	}
 }
 
-static int line6_init_cap_control_midi(struct usb_line6 *line6)
+static int line6_init_cap_control(struct usb_line6 *line6)
 {
 	int ret;
 
@@ -524,14 +531,16 @@ static int line6_init_cap_control_midi(struct usb_line6 *line6)
 	if (!line6->buffer_listen)
 		return -ENOMEM;
 
-	line6->buffer_message = kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL);
-	if (!line6->buffer_message)
-		return -ENOMEM;
-
 	line6->urb_listen = usb_alloc_urb(0, GFP_KERNEL);
 	if (!line6->urb_listen)
 		return -ENOMEM;
 
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+		line6->buffer_message = kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL);
+		if (!line6->buffer_message)
+			return -ENOMEM;
+	}
+
 	ret = line6_start_listen(line6);
 	if (ret < 0) {
 		dev_err(line6->ifcdev, "cannot start listening: %d\n", ret);
@@ -605,8 +614,8 @@ int line6_probe(struct usb_interface *interface,
 
 	line6_get_interval(line6);
 
-	if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
-		ret = line6_init_cap_control_midi(line6);
+	if (properties->capabilities & LINE6_CAP_CONTROL) {
+		ret = line6_init_cap_control(line6);
 		if (ret < 0)
 			goto error;
 	}
@@ -676,7 +685,7 @@ int line6_suspend(struct usb_interface *interface, pm_message_t message)
 
 	snd_power_change_state(line6->card, SNDRV_CTL_POWER_D3hot);
 
-	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL)
 		line6_stop_listen(line6);
 
 	if (line6pcm != NULL) {
@@ -695,7 +704,7 @@ int line6_resume(struct usb_interface *interface)
 {
 	struct usb_line6 *line6 = usb_get_intfdata(interface);
 
-	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL)
 		line6_start_listen(line6);
 
 	snd_power_change_state(line6->card, SNDRV_CTL_POWER_D0);
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index 2551cb5..71ad299 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -146,15 +146,17 @@ struct usb_line6 {
 	/* URB for listening to POD data endpoint */
 	struct urb *urb_listen;
 
-	/* Buffer for listening to POD data endpoint */
+	/* Buffer for incoming data from POD data endpoint */
 	unsigned char *buffer_listen;
 
-	/* Buffer for message to be processed */
+	/* Buffer for message to be processed, generated from MIDI layer */
 	unsigned char *buffer_message;
 
-	/* Length of message to be processed */
+	/* Length of message to be processed, generated from MIDI layer  */
 	int message_length;
 
+	/* If MIDI is supported, buffer_message contains the pre-processed data;
+	 * otherwise the data is only in urb_listen (buffer_incoming). */
 	void (*process_message)(struct usb_line6 *);
 	void (*disconnect)(struct usb_line6 *line6);
 };
diff --git a/sound/usb/line6/midi.c b/sound/usb/line6/midi.c
index cebea9b..d0fb2f2 100644
--- a/sound/usb/line6/midi.c
+++ b/sound/usb/line6/midi.c
@@ -258,7 +258,7 @@ int line6_init_midi(struct usb_line6 *line6)
 	struct snd_rawmidi *rmidi;
 	struct snd_line6_midi *line6midi;
 
-	if (!(line6->properties->capabilities & LINE6_CAP_CONTROL)) {
+	if (!(line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)) {
 		/* skip MIDI initialization and report success */
 		return 0;
 	}
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH 08/15] ALSA: line6: Cleanup initialization
  2016-08-11 19:02 [PATCH 00/15] Line6 POD X3/X3Live suport Andrej Krutak
                   ` (6 preceding siblings ...)
  2016-08-11 19:02 ` [PATCH 07/15] ALSA: line6: Allow processing of raw incoming messages Andrej Krutak
@ 2016-08-11 19:02 ` Andrej Krutak
  2016-08-11 19:02 ` [PATCH 09/15] ALSA: line6: Add hwdep interface to access the POD control messages Andrej Krutak
                   ` (10 subsequent siblings)
  18 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-08-11 19:02 UTC (permalink / raw)
  To: alsa-devel; +Cc: Andrej Krutak

Only determine control port properties if the devices needs it.
Only initialize PCM for POD HD devices that support it.
No POD HD seems to support MIDI, thus drop the initialization.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/driver.c |  3 +--
 sound/usb/line6/podhd.c  | 19 ++++++++-----------
 2 files changed, 9 insertions(+), 13 deletions(-)

diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 853a143..8a71d45 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -612,9 +612,8 @@ int line6_probe(struct usb_interface *interface,
 		goto error;
 	}
 
-	line6_get_interval(line6);
-
 	if (properties->capabilities & LINE6_CAP_CONTROL) {
+		line6_get_interval(line6);
 		ret = line6_init_cap_control(line6);
 		if (ret < 0)
 			goto error;
diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c
index f72803a..80ed138 100644
--- a/sound/usb/line6/podhd.c
+++ b/sound/usb/line6/podhd.c
@@ -341,17 +341,14 @@ static int podhd_init(struct usb_line6 *line6,
 
 	line6->disconnect = podhd_disconnect;
 
-	/* initialize MIDI subsystem: */
-	err = line6_init_midi(line6);
-	if (err < 0)
-		return err;
-
-	/* initialize PCM subsystem: */
-	err = line6_init_pcm(line6,
-		(id->driver_info == LINE6_PODX3) ? &podx3_pcm_properties :
-		&podhd_pcm_properties);
-	if (err < 0)
-		return err;
+	if (pod->line6.properties->capabilities & LINE6_CAP_PCM) {
+		/* initialize PCM subsystem: */
+		err = line6_init_pcm(line6,
+			(id->driver_info == LINE6_PODX3) ? &podx3_pcm_properties :
+			&podhd_pcm_properties);
+		if (err < 0)
+			return err;
+	}
 
 	if (!(pod->line6.properties->capabilities & LINE6_CAP_CONTROL)) {
 		/* register USB audio system directly */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH 09/15] ALSA: line6: Add hwdep interface to access the POD control messages
  2016-08-11 19:02 [PATCH 00/15] Line6 POD X3/X3Live suport Andrej Krutak
                   ` (7 preceding siblings ...)
  2016-08-11 19:02 ` [PATCH 08/15] ALSA: line6: Cleanup initialization Andrej Krutak
@ 2016-08-11 19:02 ` Andrej Krutak
  2016-08-12  8:44   ` Takashi Iwai
  2016-08-11 19:02 ` [PATCH 10/15] ALSA: line6: Add proper locks for hwdep open/release/read Andrej Krutak
                   ` (9 subsequent siblings)
  18 siblings, 1 reply; 81+ messages in thread
From: Andrej Krutak @ 2016-08-11 19:02 UTC (permalink / raw)
  To: alsa-devel; +Cc: Andrej Krutak

We must do it this way, because e.g. POD X3 won't play any sound unless
the host listens on the bulk EP, so we cannot export it only via libusb.

The driver currently doesn't use the bulk EP messages in other way,
in future it could e.g. sense/modify volume(s).

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 include/uapi/sound/asound.h |   3 +-
 sound/usb/line6/driver.c    | 167 ++++++++++++++++++++++++++++++++++++++++++++
 sound/usb/line6/driver.h    |  12 ++++
 3 files changed, 181 insertions(+), 1 deletion(-)

diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index 609cadb..be353a7 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -106,9 +106,10 @@ enum {
 	SNDRV_HWDEP_IFACE_FW_OXFW,	/* Oxford OXFW970/971 based device */
 	SNDRV_HWDEP_IFACE_FW_DIGI00X,	/* Digidesign Digi 002/003 family */
 	SNDRV_HWDEP_IFACE_FW_TASCAM,	/* TASCAM FireWire series */
+	SNDRV_HWDEP_IFACE_LINE6,	/* Line6 USB processors */
 
 	/* Don't forget to change the following: */
-	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_TASCAM
+	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_LINE6
 };
 
 struct snd_hwdep_info {
diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 8a71d45..0a0e324 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -14,9 +14,11 @@
 #include <linux/export.h>
 #include <linux/slab.h>
 #include <linux/usb.h>
+#include <linux/circ_buf.h>
 
 #include <sound/core.h>
 #include <sound/initval.h>
+#include <sound/hwdep.h>
 
 #include "capture.h"
 #include "driver.h"
@@ -315,8 +317,11 @@ static void line6_data_received(struct urb *urb)
 				line6->process_message(line6);
 		}
 	} else {
+		line6->buffer_message = urb->transfer_buffer;
+		line6->message_length = urb->actual_length;
 		if (line6->process_message)
 			line6->process_message(line6);
+		line6->buffer_message = NULL;
 	}
 
 	line6_start_listen(line6);
@@ -522,6 +527,163 @@ static void line6_get_interval(struct usb_line6 *line6)
 	}
 }
 
+
+/* Enable buffering of incoming messages, flush the buffer */
+static int line6_hwdep_open(struct snd_hwdep *hw, struct file *file)
+{
+	struct usb_line6 *line6 = hw->private_data;
+
+	/* NOTE: hwdep already provides atomicity (exclusive == true), but for
+	 * sure... */
+	if (test_and_set_bit(0, &line6->buffer_circular.active))
+		return -EBUSY;
+
+	line6->buffer_circular.head = 0;
+	line6->buffer_circular.tail = 0;
+	sema_init(&line6->buffer_circular.sem, 0);
+
+	line6->buffer_circular.active = 1;
+
+	line6->buffer_circular.data =
+		kmalloc(LINE6_MESSAGE_MAXLEN * LINE6_MESSAGE_MAXCOUNT, GFP_KERNEL);
+	if (!line6->buffer_circular.data) {
+		return -ENOMEM;
+	}
+	line6->buffer_circular.data_len =
+		kmalloc(sizeof(*line6->buffer_circular.data_len) *
+			LINE6_MESSAGE_MAXCOUNT, GFP_KERNEL);
+	if (!line6->buffer_circular.data_len) {
+		kfree(line6->buffer_circular.data);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/* Stop buffering */
+static int line6_hwdep_release(struct snd_hwdep *hw, struct file *file)
+{
+	struct usb_line6 *line6 = hw->private_data;
+
+	/* By this time, no readers are waiting, we can safely recreate the
+	 * semaphore at next open. */
+	line6->buffer_circular.active = 0;
+
+	kfree(line6->buffer_circular.data);
+	kfree(line6->buffer_circular.data_len);
+	return 0;
+}
+
+/* Read from circular buffer, return to user */
+static long
+line6_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+					loff_t *offset)
+{
+	struct usb_line6 *line6 = hwdep->private_data;
+	unsigned long tail;
+
+	if (down_interruptible(&line6->buffer_circular.sem)) {
+		return -ERESTARTSYS;
+	}
+	/* There must an item now in the buffer... */
+
+	tail = line6->buffer_circular.tail;
+
+	if (line6->buffer_circular.data_len[tail] > count) {
+		/* Buffer too small; allow re-read of the current item... */
+		up(&line6->buffer_circular.sem);
+		return -EINVAL;
+	}
+
+	if (copy_to_user(buf,
+		&line6->buffer_circular.data[tail * LINE6_MESSAGE_MAXLEN],
+		line6->buffer_circular.data_len[tail])
+	) {
+		rv = -EFAULT;
+		goto end;
+	} else {
+		rv = line6->buffer_circular.data_len[tail];
+	}
+
+	smp_store_release(&line6->buffer_circular.tail,
+				(tail + 1) & (LINE6_MESSAGE_MAXCOUNT - 1));
+
+	return 0;
+}
+
+/* Write directly (no buffering) to device by user*/
+static long
+line6_hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count,
+					loff_t *offset)
+{
+	struct usb_line6 *line6 = hwdep->private_data;
+	int rv;
+	char *data_copy;
+
+	data_copy = kmalloc(count, GFP_ATOMIC);
+	if (!data_copy)
+		return -ENOMEM;
+
+	if (copy_from_user(data_copy, data, count))
+		rv = -EFAULT;
+	else
+		rv = line6_send_raw_message(line6, data_copy, count);
+
+	kfree(data_copy);
+	return rv;
+}
+
+static const struct snd_hwdep_ops hwdep_ops = {
+	.open    = line6_hwdep_open,
+	.release = line6_hwdep_release,
+	.read    = line6_hwdep_read,
+	.write   = line6_hwdep_write,
+};
+
+/* Insert into circular buffer */
+static void line6_hwdep_push_message(struct usb_line6 *line6)
+{
+	unsigned long head = line6->buffer_circular.head;
+	/* The spin_unlock() and next spin_lock() provide needed ordering. */
+	unsigned long tail = ACCESS_ONCE(line6->buffer_circular.tail);
+
+	if (!line6->buffer_circular.active)
+		return;
+
+	if (CIRC_SPACE(head, tail, LINE6_MESSAGE_MAXCOUNT) >= 1) {
+		unsigned char *item = &line6->buffer_circular.data[
+			head * LINE6_MESSAGE_MAXLEN];
+		memcpy(item, line6->buffer_message, line6->message_length);
+		line6->buffer_circular.data_len[head] = line6->message_length;
+
+		smp_store_release(&line6->buffer_circular.head,
+				  (head + 1) & (LINE6_MESSAGE_MAXCOUNT - 1));
+		up(&line6->buffer_circular.sem);
+	}
+}
+
+static int line6_hwdep_init(struct usb_line6 *line6)
+{
+	int err;
+	struct snd_hwdep *hwdep;
+
+	/* TODO: usb_driver_claim_interface(); */
+	line6->process_message = line6_hwdep_push_message;
+
+	line6->buffer_circular.active = 0;
+	err = snd_hwdep_new(line6->card, "config", 0, &hwdep);
+	if (err < 0)
+		goto end;
+	strcpy(hwdep->name, "config");
+	hwdep->iface = SNDRV_HWDEP_IFACE_LINE6;
+	hwdep->ops = hwdep_ops;
+	hwdep->private_data = line6;
+	hwdep->exclusive = true;
+
+end:
+	return err;
+}
+
 static int line6_init_cap_control(struct usb_line6 *line6)
 {
 	int ret;
@@ -539,6 +701,10 @@ static int line6_init_cap_control(struct usb_line6 *line6)
 		line6->buffer_message = kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL);
 		if (!line6->buffer_message)
 			return -ENOMEM;
+	} else {
+		ret = line6_hwdep_init(line6);
+		if (ret < 0)
+			return ret;
 	}
 
 	ret = line6_start_listen(line6);
@@ -716,3 +882,4 @@ EXPORT_SYMBOL_GPL(line6_resume);
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
+
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index 71ad299..f4ab6cd 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -33,6 +33,8 @@
 #define LINE6_TIMEOUT (1)
 #define LINE6_BUFSIZE_LISTEN (64)
 #define LINE6_MESSAGE_MAXLEN (256)
+/* Must be 2^n; 4k packets are common, MAXLEN * MAXCOUNT should be bigger... */
+#define LINE6_MESSAGE_MAXCOUNT (1 << 5)
 
 /*
 	Line 6 MIDI control commands
@@ -155,6 +157,16 @@ struct usb_line6 {
 	/* Length of message to be processed, generated from MIDI layer  */
 	int message_length;
 
+	/* Circular buffer for non-MIDI control messages */
+	struct {
+		int active;
+		char *data;
+		int *data_len;
+		unsigned long head, tail;
+		/* Actually is up'd # of items in the buffer - times */
+		struct semaphore sem;
+	} buffer_circular;
+
 	/* If MIDI is supported, buffer_message contains the pre-processed data;
 	 * otherwise the data is only in urb_listen (buffer_incoming). */
 	void (*process_message)(struct usb_line6 *);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH 10/15] ALSA: line6: Add proper locks for hwdep open/release/read
  2016-08-11 19:02 [PATCH 00/15] Line6 POD X3/X3Live suport Andrej Krutak
                   ` (8 preceding siblings ...)
  2016-08-11 19:02 ` [PATCH 09/15] ALSA: line6: Add hwdep interface to access the POD control messages Andrej Krutak
@ 2016-08-11 19:02 ` Andrej Krutak
  2016-08-12  8:44   ` Takashi Iwai
  2016-08-11 19:02 ` [PATCH 11/15] ALSA: line6: Only free buffer if it is set Andrej Krutak
                   ` (8 subsequent siblings)
  18 siblings, 1 reply; 81+ messages in thread
From: Andrej Krutak @ 2016-08-11 19:02 UTC (permalink / raw)
  To: alsa-devel; +Cc: Andrej Krutak

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/driver.c | 16 +++++++++++-----
 sound/usb/line6/driver.h |  3 ++-
 2 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 0a0e324..344465f 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -541,8 +541,7 @@ static int line6_hwdep_open(struct snd_hwdep *hw, struct file *file)
 	line6->buffer_circular.head = 0;
 	line6->buffer_circular.tail = 0;
 	sema_init(&line6->buffer_circular.sem, 0);
-
-	line6->buffer_circular.active = 1;
+	spin_lock_init(&line6->buffer_circular.read_lock);
 
 	line6->buffer_circular.data =
 		kmalloc(LINE6_MESSAGE_MAXLEN * LINE6_MESSAGE_MAXCOUNT, GFP_KERNEL);
@@ -567,7 +566,7 @@ static int line6_hwdep_release(struct snd_hwdep *hw, struct file *file)
 
 	/* By this time, no readers are waiting, we can safely recreate the
 	 * semaphore at next open. */
-	line6->buffer_circular.active = 0;
+	clear_bit(0, &line6->buffer_circular.active);
 
 	kfree(line6->buffer_circular.data);
 	kfree(line6->buffer_circular.data_len);
@@ -581,18 +580,23 @@ line6_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
 {
 	struct usb_line6 *line6 = hwdep->private_data;
 	unsigned long tail;
+	long rv = 0;
 
 	if (down_interruptible(&line6->buffer_circular.sem)) {
 		return -ERESTARTSYS;
 	}
 	/* There must an item now in the buffer... */
 
+	spin_lock(&line6->buffer_circular.read_lock);
+
 	tail = line6->buffer_circular.tail;
 
 	if (line6->buffer_circular.data_len[tail] > count) {
 		/* Buffer too small; allow re-read of the current item... */
 		up(&line6->buffer_circular.sem);
-		return -EINVAL;
+
+		rv = -EINVAL;
+		goto end;
 	}
 
 	if (copy_to_user(buf,
@@ -608,7 +612,9 @@ line6_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
 	smp_store_release(&line6->buffer_circular.tail,
 				(tail + 1) & (LINE6_MESSAGE_MAXCOUNT - 1));
 
-	return 0;
+end:
+	spin_unlock(&line6->buffer_circular.read_lock);
+	return rv;
 }
 
 /* Write directly (no buffering) to device by user*/
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index f4ab6cd..05e155c 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -159,12 +159,13 @@ struct usb_line6 {
 
 	/* Circular buffer for non-MIDI control messages */
 	struct {
-		int active;
+		unsigned long active;
 		char *data;
 		int *data_len;
 		unsigned long head, tail;
 		/* Actually is up'd # of items in the buffer - times */
 		struct semaphore sem;
+		spinlock_t read_lock;
 	} buffer_circular;
 
 	/* If MIDI is supported, buffer_message contains the pre-processed data;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH 11/15] ALSA: line6: Only free buffer if it is set.
  2016-08-11 19:02 [PATCH 00/15] Line6 POD X3/X3Live suport Andrej Krutak
                   ` (9 preceding siblings ...)
  2016-08-11 19:02 ` [PATCH 10/15] ALSA: line6: Add proper locks for hwdep open/release/read Andrej Krutak
@ 2016-08-11 19:02 ` Andrej Krutak
  2016-08-12  8:45   ` Takashi Iwai
  2016-08-11 19:02 ` [PATCH 12/15] ALSA: line6: Give up on the lock while URBs are released Andrej Krutak
                   ` (7 subsequent siblings)
  18 siblings, 1 reply; 81+ messages in thread
From: Andrej Krutak @ 2016-08-11 19:02 UTC (permalink / raw)
  To: alsa-devel; +Cc: Andrej Krutak

This makes the resource freeing not dependent on the existence of private data
from the podhd module, which may be gone during the call.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/driver.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 344465f..142e0e3 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -479,13 +479,14 @@ static void line6_destruct(struct snd_card *card)
 	struct usb_device *usbdev = line6->usbdev;
 
 	/* free buffer memory first: */
-	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)
+	if (line6->buffer_message)
 		kfree(line6->buffer_message);
 
 	kfree(line6->buffer_listen);
 
 	/* then free URBs: */
 	usb_free_urb(line6->urb_listen);
+	line6->urb_listen = NULL;
 
 	/* decrement reference counters: */
 	usb_put_dev(usbdev);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH 12/15] ALSA: line6: Give up on the lock while URBs are released.
  2016-08-11 19:02 [PATCH 00/15] Line6 POD X3/X3Live suport Andrej Krutak
                   ` (10 preceding siblings ...)
  2016-08-11 19:02 ` [PATCH 11/15] ALSA: line6: Only free buffer if it is set Andrej Krutak
@ 2016-08-11 19:02 ` Andrej Krutak
  2016-08-12  8:45   ` Takashi Iwai
  2016-08-11 19:02 ` [PATCH 13/15] ALSA: line6: Add support for POD X3 Live (only USB ID differs from POD X3) Andrej Krutak
                   ` (6 subsequent siblings)
  18 siblings, 1 reply; 81+ messages in thread
From: Andrej Krutak @ 2016-08-11 19:02 UTC (permalink / raw)
  To: alsa-devel; +Cc: Andrej Krutak

Done, because line6_stream_stop() locks and calls line6_unlink_audio_urbs(),
which in turn invokes audio_out_callback(), which tries to lock 2nd time.

Fixes:

=============================================
[ INFO: possible recursive locking detected ]
4.4.15+ #15 Not tainted
---------------------------------------------
mplayer/3591 is trying to acquire lock:
 (&(&line6pcm->out.lock)->rlock){-.-...}, at: [<bfa27655>] audio_out_callback+0x70/0x110 [snd_usb_line6]

but task is already holding lock:
 (&(&line6pcm->out.lock)->rlock){-.-...}, at: [<bfa26aad>] line6_stream_stop+0x24/0x5c [snd_usb_line6]

other info that might help us debug this:
 Possible unsafe locking scenario:

       CPU0
       ----
  lock(&(&line6pcm->out.lock)->rlock);
  lock(&(&line6pcm->out.lock)->rlock);

 *** DEADLOCK ***

 May be due to missing lock nesting notation

3 locks held by mplayer/3591:
 #0:  (snd_pcm_link_rwlock){.-.-..}, at: [<bf8d49a7>] snd_pcm_stream_lock+0x1e/0x40 [snd_pcm]
 #1:  (&(&substream->self_group.lock)->rlock){-.-...}, at: [<bf8d49af>] snd_pcm_stream_lock+0x26/0x40 [snd_pcm]
 #2:  (&(&line6pcm->out.lock)->rlock){-.-...}, at: [<bfa26aad>] line6_stream_stop+0x24/0x5c [snd_usb_line6]

stack backtrace:
CPU: 0 PID: 3591 Comm: mplayer Not tainted 4.4.15+ #15
Hardware name: Generic AM33XX (Flattened Device Tree)
[<c0015d85>] (unwind_backtrace) from [<c001253d>] (show_stack+0x11/0x14)
[<c001253d>] (show_stack) from [<c02f1bdf>] (dump_stack+0x8b/0xac)
[<c02f1bdf>] (dump_stack) from [<c0076f43>] (__lock_acquire+0xc8b/0x1780)
[<c0076f43>] (__lock_acquire) from [<c007810d>] (lock_acquire+0x99/0x1c0)
[<c007810d>] (lock_acquire) from [<c06171e7>] (_raw_spin_lock_irqsave+0x3f/0x4c)
[<c06171e7>] (_raw_spin_lock_irqsave) from [<bfa27655>] (audio_out_callback+0x70/0x110 [snd_usb_line6])
[<bfa27655>] (audio_out_callback [snd_usb_line6]) from [<c04294db>] (__usb_hcd_giveback_urb+0x53/0xd0)
[<c04294db>] (__usb_hcd_giveback_urb) from [<c046388d>] (musb_giveback+0x3d/0x98)
[<c046388d>] (musb_giveback) from [<c04647f5>] (musb_urb_dequeue+0x6d/0x114)
[<c04647f5>] (musb_urb_dequeue) from [<c042ac11>] (usb_hcd_unlink_urb+0x39/0x98)
[<c042ac11>] (usb_hcd_unlink_urb) from [<bfa26a87>] (line6_unlink_audio_urbs+0x6a/0x6c [snd_usb_line6])
[<bfa26a87>] (line6_unlink_audio_urbs [snd_usb_line6]) from [<bfa26acb>] (line6_stream_stop+0x42/0x5c [snd_usb_line6])
[<bfa26acb>] (line6_stream_stop [snd_usb_line6]) from [<bfa26fe7>] (snd_line6_trigger+0xb6/0xf4 [snd_usb_line6])
[<bfa26fe7>] (snd_line6_trigger [snd_usb_line6]) from [<bf8d47b7>] (snd_pcm_do_stop+0x36/0x38 [snd_pcm])
[<bf8d47b7>] (snd_pcm_do_stop [snd_pcm]) from [<bf8d462f>] (snd_pcm_action_single+0x22/0x40 [snd_pcm])
[<bf8d462f>] (snd_pcm_action_single [snd_pcm]) from [<bf8d46f9>] (snd_pcm_action+0xac/0xb0 [snd_pcm])
[<bf8d46f9>] (snd_pcm_action [snd_pcm]) from [<bf8d4b61>] (snd_pcm_drop+0x38/0x64 [snd_pcm])
[<bf8d4b61>] (snd_pcm_drop [snd_pcm]) from [<bf8d6233>] (snd_pcm_common_ioctl1+0x7fe/0xbe8 [snd_pcm])
[<bf8d6233>] (snd_pcm_common_ioctl1 [snd_pcm]) from [<bf8d6779>] (snd_pcm_playback_ioctl1+0x15c/0x51c [snd_pcm])
[<bf8d6779>] (snd_pcm_playback_ioctl1 [snd_pcm]) from [<bf8d6b59>] (snd_pcm_playback_ioctl+0x20/0x28 [snd_pcm])
[<bf8d6b59>] (snd_pcm_playback_ioctl [snd_pcm]) from [<c016714b>] (do_vfs_ioctl+0x3af/0x5c8)

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/pcm.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c
index 0af6d4e..13b3b27 100644
--- a/sound/usb/line6/pcm.c
+++ b/sound/usb/line6/pcm.c
@@ -216,7 +216,9 @@ static void line6_stream_stop(struct snd_line6_pcm *line6pcm, int direction,
 	spin_lock_irqsave(&pstr->lock, flags);
 	clear_bit(type, &pstr->running);
 	if (!pstr->running) {
+		spin_unlock_irqrestore(&pstr->lock, flags);
 		line6_unlink_audio_urbs(line6pcm, pstr);
+		spin_lock_irqsave(&pstr->lock, flags);
 		if (direction == SNDRV_PCM_STREAM_CAPTURE) {
 			line6pcm->prev_fbuf = NULL;
 			line6pcm->prev_fsize = 0;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH 13/15] ALSA: line6: Add support for POD X3 Live (only USB ID differs from POD X3)
  2016-08-11 19:02 [PATCH 00/15] Line6 POD X3/X3Live suport Andrej Krutak
                   ` (11 preceding siblings ...)
  2016-08-11 19:02 ` [PATCH 12/15] ALSA: line6: Give up on the lock while URBs are released Andrej Krutak
@ 2016-08-11 19:02 ` Andrej Krutak
  2016-08-11 19:02 ` [PATCH 14/15] ALSA: line6: Give up hwdep spinlock temporarily during read operation Andrej Krutak
                   ` (5 subsequent siblings)
  18 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-08-11 19:02 UTC (permalink / raw)
  To: alsa-devel; +Cc: Andrej Krutak

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/podhd.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c
index 80ed138..be764f8 100644
--- a/sound/usb/line6/podhd.c
+++ b/sound/usb/line6/podhd.c
@@ -37,6 +37,7 @@ enum {
 	LINE6_PODHD500_0,
 	LINE6_PODHD500_1,
 	LINE6_PODX3,
+	LINE6_PODX3LIVE
 };
 
 struct usb_line6_podhd {
@@ -373,6 +374,7 @@ static const struct usb_device_id podhd_id_table[] = {
 	{ LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500_0 },
 	{ LINE6_IF_NUM(0x414D, 1), .driver_info = LINE6_PODHD500_1 },
 	{ LINE6_IF_NUM(0x414A, 0), .driver_info = LINE6_PODX3 },
+	{ LINE6_IF_NUM(0x414B, 0), .driver_info = LINE6_PODX3LIVE },
 	{}
 };
 
@@ -434,6 +436,17 @@ static const struct line6_properties podhd_properties_table[] = {
 		.ep_audio_r = 0x86,
 		.ep_audio_w = 0x02,
 	},
+	[LINE6_PODX3LIVE] = {
+		.id = "PODX3LIVE",
+		.name = "POD X3 LIVE",
+		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_PCM | LINE6_CAP_HWMON | LINE6_CAP_IN_NEEDS_OUT,
+		.altsetting = 1,
+		.ep_ctrl_r = 0x81,
+		.ep_ctrl_w = 0x01,
+		.ep_audio_r = 0x86,
+		.ep_audio_w = 0x02,
+	},
 };
 
 /*
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH 14/15] ALSA: line6: Give up hwdep spinlock temporarily during read operation
  2016-08-11 19:02 [PATCH 00/15] Line6 POD X3/X3Live suport Andrej Krutak
                   ` (12 preceding siblings ...)
  2016-08-11 19:02 ` [PATCH 13/15] ALSA: line6: Add support for POD X3 Live (only USB ID differs from POD X3) Andrej Krutak
@ 2016-08-11 19:02 ` Andrej Krutak
  2016-08-12  8:46   ` Takashi Iwai
  2016-08-11 19:02 ` [PATCH 15/15] ALSA: line6: Remove double line6_pcm_release() after failed acquire Andrej Krutak
                   ` (4 subsequent siblings)
  18 siblings, 1 reply; 81+ messages in thread
From: Andrej Krutak @ 2016-08-11 19:02 UTC (permalink / raw)
  To: alsa-devel; +Cc: Andrej Krutak

Without it, the code hits the 'might_sleep while atomic' debug warning.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/driver.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 142e0e3..286b6e1 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -600,6 +600,10 @@ line6_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
 		goto end;
 	}
 
+	/* Release lock while copying the stuff. Since there is only one reader,
+	 * the data is going nowhere, so this should be safe. */
+	spin_unlock(&line6->buffer_circular.read_lock);
+
 	if (copy_to_user(buf,
 		&line6->buffer_circular.data[tail * LINE6_MESSAGE_MAXLEN],
 		line6->buffer_circular.data_len[tail])
@@ -610,6 +614,7 @@ line6_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
 		rv = line6->buffer_circular.data_len[tail];
 	}
 
+	spin_lock(&line6->buffer_circular.read_lock);
 	smp_store_release(&line6->buffer_circular.tail,
 				(tail + 1) & (LINE6_MESSAGE_MAXCOUNT - 1));
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH 15/15] ALSA: line6: Remove double line6_pcm_release() after failed acquire.
  2016-08-11 19:02 [PATCH 00/15] Line6 POD X3/X3Live suport Andrej Krutak
                   ` (13 preceding siblings ...)
  2016-08-11 19:02 ` [PATCH 14/15] ALSA: line6: Give up hwdep spinlock temporarily during read operation Andrej Krutak
@ 2016-08-11 19:02 ` Andrej Krutak
  2016-08-12  8:46   ` Takashi Iwai
  2016-08-12  8:14 ` [PATCH 00/15] Line6 POD X3/X3Live suport Takashi Iwai
                   ` (3 subsequent siblings)
  18 siblings, 1 reply; 81+ messages in thread
From: Andrej Krutak @ 2016-08-11 19:02 UTC (permalink / raw)
  To: alsa-devel; +Cc: Andrej Krutak

If there's an error, pcm is released in line6_pcm_acquire already.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/pcm.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c
index 13b3b27..455244c 100644
--- a/sound/usb/line6/pcm.c
+++ b/sound/usb/line6/pcm.c
@@ -55,7 +55,6 @@ static int snd_line6_impulse_volume_put(struct snd_kcontrol *kcontrol,
 		err = line6_pcm_acquire(line6pcm, LINE6_STREAM_IMPULSE, true);
 		if (err < 0) {
 			line6pcm->impulse_volume = 0;
-			line6_pcm_release(line6pcm, LINE6_STREAM_IMPULSE);
 			return err;
 		}
 	} else {
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* Re: [PATCH 00/15] Line6 POD X3/X3Live suport
  2016-08-11 19:02 [PATCH 00/15] Line6 POD X3/X3Live suport Andrej Krutak
                   ` (14 preceding siblings ...)
  2016-08-11 19:02 ` [PATCH 15/15] ALSA: line6: Remove double line6_pcm_release() after failed acquire Andrej Krutak
@ 2016-08-12  8:14 ` Takashi Iwai
  2016-08-12 10:31   ` Andrej Kruták
  2016-08-18 22:20 ` [PATCH v2 0/9] " Andrej Krutak
                   ` (2 subsequent siblings)
  18 siblings, 1 reply; 81+ messages in thread
From: Takashi Iwai @ 2016-08-12  8:14 UTC (permalink / raw)
  To: Andrej Krutak; +Cc: alsa-devel

On Thu, 11 Aug 2016 21:02:12 +0200,
Andrej Krutak wrote:
> 
> Hello all,
> 
> 
> attached is the first version of the driver, based on POD HD. They share a lot
> of similarities, but at the same time there were some generalizations needed. I
> think POD HD could follow up on these patches to add hwdep support, perhaps
> helix too... I tried to not introduce regressions to the old HW.
> 
> Anyhow, this is probably not the final version of the patches, as it is my
> first submission to upstream - I'm sure there will be some issues.
> Especially the patch
> "Use device_create_file instead of snd_card_add_dev_attr" I have to revisit,
> it may not be necessarry in the end.
> 
> Other than that, the patches mostly pass `make checkpatches`. I've been
> testing the driver for a while now, including lock debugging options etc.,
> and there don't seem to be functional problems.
> 
> There's one missing thing - the driver uses bulk USB interface of the
> device, but so far I wasn't able to make the usb_driver_claim_interface()
> work... I hope this can be added later, if someone (or I) finds time.
> 
> 
> Thanks for your inputs, greetings,

Thanks for the patches.  Many of them look good, but we'd need
revisiting some of them.  But at the next time, please put maintainers
to Cc, not only to sending to ML.

I'll comment on each patch.


Takashi

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH 01/15] ALSA: line6: Make driver configuration more generic.
  2016-08-11 19:02 ` [PATCH 01/15] ALSA: line6: Make driver configuration more generic Andrej Krutak
@ 2016-08-12  8:24   ` Takashi Iwai
  0 siblings, 0 replies; 81+ messages in thread
From: Takashi Iwai @ 2016-08-12  8:24 UTC (permalink / raw)
  To: Andrej Krutak; +Cc: alsa-devel

On Thu, 11 Aug 2016 21:02:13 +0200,
Andrej Krutak wrote:
> 
> The main reasons are different settings for USB low/high speed and possible
> different channel counts for in/out; required by POD X3.
> 
> This includes:
> * iso_buffers (count of iso buffers depends on USB speed, 2 is not enough
>   for high speed)
> * bytes_per_frame -> bytes_per_channel
> * max_packet_size -> max_packet_size_in/out
> * USB_INTERVALS_PER_SECOND -> LOW/HIGH settings
>   (high needs 8000, instead of 1000)

The changes are slightly too many done in a shot.  It'd be great if
you can split to a few each logical change if possible.


> --- a/sound/usb/line6/capture.c
> +++ b/sound/usb/line6/capture.c
.....
> @@ -173,17 +175,26 @@ static void audio_in_callback(struct urb *urb)
>  		fbuf = urb->transfer_buffer + fin->offset;
>  		fsize = fin->actual_length;
>  
> -		if (fsize > line6pcm->max_packet_size) {
> +		if (fsize > line6pcm->max_packet_size_in) {
>  			dev_err(line6pcm->line6->ifcdev,
>  				"driver and/or device bug: packet too large (%d > %d)\n",
> -				fsize, line6pcm->max_packet_size);
> +				fsize, line6pcm->max_packet_size_in);
>  		}
>  
>  		length += fsize;
>  
> -		/* the following assumes LINE6_ISO_PACKETS == 1: */
> +#if LINE6_ISO_PACKETS != 1
> +# error "The following assumes LINE6_ISO_PACKETS == 1"
> +/* TODO:
> +   Also, if iso_buffers != 2, the prev frame is almost random at playback side.
> +   This needs to be redesigned. It should be "stable", but we may experience
> +   sync problems on such high-speed configs.
> +*/
> +#endif

You can use BUILD_BUG_ON().



> --- a/sound/usb/line6/driver.h
> +++ b/sound/usb/line6/driver.h
> @@ -18,39 +18,45 @@
>  
>  #include "midi.h"
>  
> -#define USB_INTERVALS_PER_SECOND 1000
> +/* USB 1.1 speed configuration */
> +#define USB_LOW_INTERVALS_PER_SECOND (1000)
> +#define USB_LOW_ISO_BUFFERS (2)
> +
> +/* USB 2.0+ speed configuration */
> +#define USB_HIGH_INTERVALS_PER_SECOND (8000)
> +#define USB_HIGH_ISO_BUFFERS (16)

Superfluous parentheses (all other places, too).


> --- a/sound/usb/line6/pcm.c
> +++ b/sound/usb/line6/pcm.c
....
> @@ -433,24 +438,26 @@ static struct snd_kcontrol_new line6_controls[] = {
>  /*
>  	Cleanup the PCM device.
>  */
> -static void cleanup_urbs(struct line6_pcm_stream *pcms)
> +static void cleanup_urbs(struct line6_pcm_stream *pcms, int iso_buffers)
>  {
>  	int i;
>  
> -	for (i = 0; i < LINE6_ISO_BUFFERS; i++) {
> +	for (i = 0; i < iso_buffers; i++) {
>  		if (pcms->urbs[i]) {

This may cause NULL-dereference when pcms->urbs wasn't allocated.


thanks,

Takashi

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH 05/15] ALSA: line6: Use device_create_file instead of snd_card_add_dev_attr
  2016-08-11 19:02 ` [PATCH 05/15] ALSA: line6: Use device_create_file instead of snd_card_add_dev_attr Andrej Krutak
@ 2016-08-12  8:26   ` Takashi Iwai
  0 siblings, 0 replies; 81+ messages in thread
From: Takashi Iwai @ 2016-08-12  8:26 UTC (permalink / raw)
  To: Andrej Krutak; +Cc: alsa-devel

On Thu, 11 Aug 2016 21:02:17 +0200,
Andrej Krutak wrote:
> 
> The latter seems to create invalid configuration, the device pointer passed
> to the attr callbacks is not correct afterwards (results into segfaults when
> accessing the attributes).

As you already considered, this change is moving to a wrong
direction.  The explicit call of device_create_file() should be
reduced as much as possible in general.


Takashi

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH 09/15] ALSA: line6: Add hwdep interface to access the POD control messages
  2016-08-11 19:02 ` [PATCH 09/15] ALSA: line6: Add hwdep interface to access the POD control messages Andrej Krutak
@ 2016-08-12  8:44   ` Takashi Iwai
  2016-08-12 11:10     ` Andrej Kruták
  0 siblings, 1 reply; 81+ messages in thread
From: Takashi Iwai @ 2016-08-12  8:44 UTC (permalink / raw)
  To: Andrej Krutak; +Cc: alsa-devel

On Thu, 11 Aug 2016 21:02:21 +0200,
Andrej Krutak wrote:
> 
> We must do it this way, because e.g. POD X3 won't play any sound unless
> the host listens on the bulk EP, so we cannot export it only via libusb.
> 
> The driver currently doesn't use the bulk EP messages in other way,
> in future it could e.g. sense/modify volume(s).
> 
> Signed-off-by: Andrej Krutak <dev@andree.sk>
> ---
>  include/uapi/sound/asound.h |   3 +-
>  sound/usb/line6/driver.c    | 167 ++++++++++++++++++++++++++++++++++++++++++++
>  sound/usb/line6/driver.h    |  12 ++++
>  3 files changed, 181 insertions(+), 1 deletion(-)
> 
> diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
> index 609cadb..be353a7 100644
> --- a/include/uapi/sound/asound.h
> +++ b/include/uapi/sound/asound.h
> @@ -106,9 +106,10 @@ enum {
>  	SNDRV_HWDEP_IFACE_FW_OXFW,	/* Oxford OXFW970/971 based device */
>  	SNDRV_HWDEP_IFACE_FW_DIGI00X,	/* Digidesign Digi 002/003 family */
>  	SNDRV_HWDEP_IFACE_FW_TASCAM,	/* TASCAM FireWire series */
> +	SNDRV_HWDEP_IFACE_LINE6,	/* Line6 USB processors */
>  
>  	/* Don't forget to change the following: */
> -	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_TASCAM
> +	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_LINE6
>  };
>  
>  struct snd_hwdep_info {
> diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
> index 8a71d45..0a0e324 100644
> --- a/sound/usb/line6/driver.c
> +++ b/sound/usb/line6/driver.c
> @@ -14,9 +14,11 @@
>  #include <linux/export.h>
>  #include <linux/slab.h>
>  #include <linux/usb.h>
> +#include <linux/circ_buf.h>
>  
>  #include <sound/core.h>
>  #include <sound/initval.h>
> +#include <sound/hwdep.h>
>  
>  #include "capture.h"
>  #include "driver.h"
> @@ -315,8 +317,11 @@ static void line6_data_received(struct urb *urb)
>  				line6->process_message(line6);
>  		}
>  	} else {
> +		line6->buffer_message = urb->transfer_buffer;
> +		line6->message_length = urb->actual_length;
>  		if (line6->process_message)
>  			line6->process_message(line6);
> +		line6->buffer_message = NULL;
>  	}
>  
>  	line6_start_listen(line6);
> @@ -522,6 +527,163 @@ static void line6_get_interval(struct usb_line6 *line6)
>  	}
>  }
>  
> +
> +/* Enable buffering of incoming messages, flush the buffer */
> +static int line6_hwdep_open(struct snd_hwdep *hw, struct file *file)
> +{
> +	struct usb_line6 *line6 = hw->private_data;
> +
> +	/* NOTE: hwdep already provides atomicity (exclusive == true), but for
> +	 * sure... */
> +	if (test_and_set_bit(0, &line6->buffer_circular.active))
> +		return -EBUSY;
> +
> +	line6->buffer_circular.head = 0;
> +	line6->buffer_circular.tail = 0;
> +	sema_init(&line6->buffer_circular.sem, 0);

Why do you use semaphore, not mutex?  And initializing at this
point...?  It looks racy.


> +	line6->buffer_circular.active = 1;

Looks superfluous, done in the above?


> +	line6->buffer_circular.data =
> +		kmalloc(LINE6_MESSAGE_MAXLEN * LINE6_MESSAGE_MAXCOUNT, GFP_KERNEL);
> +	if (!line6->buffer_circular.data) {
> +		return -ENOMEM;
> +	}
> +	line6->buffer_circular.data_len =
> +		kmalloc(sizeof(*line6->buffer_circular.data_len) *
> +			LINE6_MESSAGE_MAXCOUNT, GFP_KERNEL);
> +	if (!line6->buffer_circular.data_len) {
> +		kfree(line6->buffer_circular.data);
> +		return -ENOMEM;
> +	}
> +
> +	return 0;
> +}
> +
> +/* Stop buffering */
> +static int line6_hwdep_release(struct snd_hwdep *hw, struct file *file)
> +{
> +	struct usb_line6 *line6 = hw->private_data;
> +
> +	/* By this time, no readers are waiting, we can safely recreate the
> +	 * semaphore at next open. */
> +	line6->buffer_circular.active = 0;
> +
> +	kfree(line6->buffer_circular.data);
> +	kfree(line6->buffer_circular.data_len);
> +	return 0;
> +}
> +
> +/* Read from circular buffer, return to user */
> +static long
> +line6_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
> +					loff_t *offset)
> +{
> +	struct usb_line6 *line6 = hwdep->private_data;
> +	unsigned long tail;

No need to use long for FIFO index.


> +	if (down_interruptible(&line6->buffer_circular.sem)) {
> +		return -ERESTARTSYS;
> +	}
> +	/* There must an item now in the buffer... */
> +
> +	tail = line6->buffer_circular.tail;
> +
> +	if (line6->buffer_circular.data_len[tail] > count) {
> +		/* Buffer too small; allow re-read of the current item... */
> +		up(&line6->buffer_circular.sem);
> +		return -EINVAL;
> +	}
> +
> +	if (copy_to_user(buf,
> +		&line6->buffer_circular.data[tail * LINE6_MESSAGE_MAXLEN],
> +		line6->buffer_circular.data_len[tail])
> +	) {
> +		rv = -EFAULT;
> +		goto end;
> +	} else {
> +		rv = line6->buffer_circular.data_len[tail];
> +	}
> +
> +	smp_store_release(&line6->buffer_circular.tail,
> +				(tail + 1) & (LINE6_MESSAGE_MAXCOUNT - 1));
> +
> +	return 0;
> +}
> +
> +/* Write directly (no buffering) to device by user*/
> +static long
> +line6_hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count,
> +					loff_t *offset)
> +{
> +	struct usb_line6 *line6 = hwdep->private_data;
> +	int rv;
> +	char *data_copy;
> +
> +	data_copy = kmalloc(count, GFP_ATOMIC);

No need to use GFP_ATOMIC in this context.
Also, you should add the sanity check of the given size.  User may
pass any size of the write.


> +	if (!data_copy)
> +		return -ENOMEM;
> +
> +	if (copy_from_user(data_copy, data, count))
> +		rv = -EFAULT;

Maybe easier to use memdup_user() helper.

> +	else
> +		rv = line6_send_raw_message(line6, data_copy, count);
> +
> +	kfree(data_copy);
> +	return rv;
> +}
> +
> +static const struct snd_hwdep_ops hwdep_ops = {
> +	.open    = line6_hwdep_open,
> +	.release = line6_hwdep_release,
> +	.read    = line6_hwdep_read,
> +	.write   = line6_hwdep_write,
> +};
> +
> +/* Insert into circular buffer */
> +static void line6_hwdep_push_message(struct usb_line6 *line6)
> +{
> +	unsigned long head = line6->buffer_circular.head;
> +	/* The spin_unlock() and next spin_lock() provide needed ordering. */
> +	unsigned long tail = ACCESS_ONCE(line6->buffer_circular.tail);
> +
> +	if (!line6->buffer_circular.active)
> +		return;
> +
> +	if (CIRC_SPACE(head, tail, LINE6_MESSAGE_MAXCOUNT) >= 1) {
> +		unsigned char *item = &line6->buffer_circular.data[
> +			head * LINE6_MESSAGE_MAXLEN];
> +		memcpy(item, line6->buffer_message, line6->message_length);
> +		line6->buffer_circular.data_len[head] = line6->message_length;
> +
> +		smp_store_release(&line6->buffer_circular.head,
> +				  (head + 1) & (LINE6_MESSAGE_MAXCOUNT - 1));
> +		up(&line6->buffer_circular.sem);
> +	}

Hmm...  this kind of a simple FIFO can be seen in anywhere in the
kernel code, and I'm sure that you can find an easier way to implement
it.  The whole code looks a bit scary as it being home-brewed.

Also, the blocking read/write control isn't usually done by a
semaphore.  Then you can handle the interrupt there.


Takashi

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH 10/15] ALSA: line6: Add proper locks for hwdep open/release/read
  2016-08-11 19:02 ` [PATCH 10/15] ALSA: line6: Add proper locks for hwdep open/release/read Andrej Krutak
@ 2016-08-12  8:44   ` Takashi Iwai
  0 siblings, 0 replies; 81+ messages in thread
From: Takashi Iwai @ 2016-08-12  8:44 UTC (permalink / raw)
  To: Andrej Krutak; +Cc: alsa-devel

On Thu, 11 Aug 2016 21:02:22 +0200,
Andrej Krutak wrote:
> 
> Signed-off-by: Andrej Krutak <dev@andree.sk>

This should be merged into the patch 9.


Takashi

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH 11/15] ALSA: line6: Only free buffer if it is set.
  2016-08-11 19:02 ` [PATCH 11/15] ALSA: line6: Only free buffer if it is set Andrej Krutak
@ 2016-08-12  8:45   ` Takashi Iwai
  0 siblings, 0 replies; 81+ messages in thread
From: Takashi Iwai @ 2016-08-12  8:45 UTC (permalink / raw)
  To: Andrej Krutak; +Cc: alsa-devel

On Thu, 11 Aug 2016 21:02:23 +0200,
Andrej Krutak wrote:
> 
> This makes the resource freeing not dependent on the existence of private data
> from the podhd module, which may be gone during the call.
> 
> Signed-off-by: Andrej Krutak <dev@andree.sk>

Should be merged to patch 9 from the beginning.


Takashi

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH 12/15] ALSA: line6: Give up on the lock while URBs are released.
  2016-08-11 19:02 ` [PATCH 12/15] ALSA: line6: Give up on the lock while URBs are released Andrej Krutak
@ 2016-08-12  8:45   ` Takashi Iwai
  2016-08-12 11:14     ` Andrej Kruták
  0 siblings, 1 reply; 81+ messages in thread
From: Takashi Iwai @ 2016-08-12  8:45 UTC (permalink / raw)
  To: Andrej Krutak; +Cc: alsa-devel

On Thu, 11 Aug 2016 21:02:24 +0200,
Andrej Krutak wrote:
> 
> Done, because line6_stream_stop() locks and calls line6_unlink_audio_urbs(),
> which in turn invokes audio_out_callback(), which tries to lock 2nd time.

Ditto, write the patch right from the beginning :)


Takashi

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH 14/15] ALSA: line6: Give up hwdep spinlock temporarily during read operation
  2016-08-11 19:02 ` [PATCH 14/15] ALSA: line6: Give up hwdep spinlock temporarily during read operation Andrej Krutak
@ 2016-08-12  8:46   ` Takashi Iwai
  0 siblings, 0 replies; 81+ messages in thread
From: Takashi Iwai @ 2016-08-12  8:46 UTC (permalink / raw)
  To: Andrej Krutak; +Cc: alsa-devel

On Thu, 11 Aug 2016 21:02:26 +0200,
Andrej Krutak wrote:
> 
> Without it, the code hits the 'might_sleep while atomic' debug warning.
> 
> Signed-off-by: Andrej Krutak <dev@andree.sk>

This also should have been folded into patch 9.


Takashi

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH 15/15] ALSA: line6: Remove double line6_pcm_release() after failed acquire.
  2016-08-11 19:02 ` [PATCH 15/15] ALSA: line6: Remove double line6_pcm_release() after failed acquire Andrej Krutak
@ 2016-08-12  8:46   ` Takashi Iwai
  0 siblings, 0 replies; 81+ messages in thread
From: Takashi Iwai @ 2016-08-12  8:46 UTC (permalink / raw)
  To: Andrej Krutak; +Cc: alsa-devel

On Thu, 11 Aug 2016 21:02:27 +0200,
Andrej Krutak wrote:
> 
> If there's an error, pcm is released in line6_pcm_acquire already.
> 
> Signed-off-by: Andrej Krutak <dev@andree.sk>

This looks like a fix independent from your patchset.
In that case, submit it individually so that I can merge it
beforehand.


Takashi


> ---
>  sound/usb/line6/pcm.c | 1 -
>  1 file changed, 1 deletion(-)
> 
> diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c
> index 13b3b27..455244c 100644
> --- a/sound/usb/line6/pcm.c
> +++ b/sound/usb/line6/pcm.c
> @@ -55,7 +55,6 @@ static int snd_line6_impulse_volume_put(struct snd_kcontrol *kcontrol,
>  		err = line6_pcm_acquire(line6pcm, LINE6_STREAM_IMPULSE, true);
>  		if (err < 0) {
>  			line6pcm->impulse_volume = 0;
> -			line6_pcm_release(line6pcm, LINE6_STREAM_IMPULSE);
>  			return err;
>  		}
>  	} else {
> -- 
> 1.9.1
> 
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel@alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
> 

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH 00/15] Line6 POD X3/X3Live suport
  2016-08-12  8:14 ` [PATCH 00/15] Line6 POD X3/X3Live suport Takashi Iwai
@ 2016-08-12 10:31   ` Andrej Kruták
  0 siblings, 0 replies; 81+ messages in thread
From: Andrej Kruták @ 2016-08-12 10:31 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel

Hi!

On Fri, Aug 12, 2016 at 10:14 AM, Takashi Iwai <tiwai@suse.de> wrote:

> On Thu, 11 Aug 2016 21:02:12 +0200,
> Andrej Krutak wrote:
> >
> > Hello all,
> >
> >
> > attached is the first version of the driver, based on POD HD. They share
> a lot
> > of similarities, but at the same time there were some generalizations
> needed. I
> > think POD HD could follow up on these patches to add hwdep support,
> perhaps
> > helix too... I tried to not introduce regressions to the old HW.
> >
> > Anyhow, this is probably not the final version of the patches, as it is
> my
> > first submission to upstream - I'm sure there will be some issues.
> > Especially the patch
> > "Use device_create_file instead of snd_card_add_dev_attr" I have to
> revisit,
> > it may not be necessarry in the end.
> >
> > Other than that, the patches mostly pass `make checkpatches`. I've been
> > testing the driver for a while now, including lock debugging options
> etc.,
> > and there don't seem to be functional problems.
> >
> > There's one missing thing - the driver uses bulk USB interface of the
> > device, but so far I wasn't able to make the usb_driver_claim_interface()
> > work... I hope this can be added later, if someone (or I) finds time.
> >
> >
> > Thanks for your inputs, greetings,
>
> Thanks for the patches.  Many of them look good, but we'd need
> revisiting some of them.  But at the next time, please put maintainers
> to Cc, not only to sending to ML.
>
> I'll comment on each patch.
>
>
Thanks for the quick feedback! I'll rework the patches where obvious, and
respond to the comments where I "object"...

-- 
Andrej

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH 09/15] ALSA: line6: Add hwdep interface to access the POD control messages
  2016-08-12  8:44   ` Takashi Iwai
@ 2016-08-12 11:10     ` Andrej Kruták
  2016-08-12 12:03       ` Takashi Iwai
  0 siblings, 1 reply; 81+ messages in thread
From: Andrej Kruták @ 2016-08-12 11:10 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel

On Fri, Aug 12, 2016 at 10:44 AM, Takashi Iwai <tiwai@suse.de> wrote:
>
> On Thu, 11 Aug 2016 21:02:21 +0200,
> Andrej Krutak wrote:
> >
> > We must do it this way, because e.g. POD X3 won't play any sound unless
> > the host listens on the bulk EP, so we cannot export it only via libusb.
> >
> > The driver currently doesn't use the bulk EP messages in other way,
> > in future it could e.g. sense/modify volume(s).
> >
> > Signed-off-by: Andrej Krutak <dev@andree.sk>
> > ---
> >  include/uapi/sound/asound.h |   3 +-
> >  sound/usb/line6/driver.c    | 167 ++++++++++++++++++++++++++++++++++++++++++++
> >  sound/usb/line6/driver.h    |  12 ++++
> >  3 files changed, 181 insertions(+), 1 deletion(-)
> >
> > diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
> > index 609cadb..be353a7 100644
> > --- a/include/uapi/sound/asound.h
> > +++ b/include/uapi/sound/asound.h
> > @@ -106,9 +106,10 @@ enum {
> >       SNDRV_HWDEP_IFACE_FW_OXFW,      /* Oxford OXFW970/971 based device */
> >       SNDRV_HWDEP_IFACE_FW_DIGI00X,   /* Digidesign Digi 002/003 family */
> >       SNDRV_HWDEP_IFACE_FW_TASCAM,    /* TASCAM FireWire series */
> > +     SNDRV_HWDEP_IFACE_LINE6,        /* Line6 USB processors */
> >
> >       /* Don't forget to change the following: */
> > -     SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_TASCAM
> > +     SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_LINE6
> >  };
> >
> >  struct snd_hwdep_info {
> > diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
> > index 8a71d45..0a0e324 100644
> > --- a/sound/usb/line6/driver.c
> > +++ b/sound/usb/line6/driver.c
> > @@ -14,9 +14,11 @@
> >  #include <linux/export.h>
> >  #include <linux/slab.h>
> >  #include <linux/usb.h>
> > +#include <linux/circ_buf.h>
> >
> >  #include <sound/core.h>
> >  #include <sound/initval.h>
> > +#include <sound/hwdep.h>
> >
> >  #include "capture.h"
> >  #include "driver.h"
> > @@ -315,8 +317,11 @@ static void line6_data_received(struct urb *urb)
> >                               line6->process_message(line6);
> >               }
> >       } else {
> > +             line6->buffer_message = urb->transfer_buffer;
> > +             line6->message_length = urb->actual_length;
> >               if (line6->process_message)
> >                       line6->process_message(line6);
> > +             line6->buffer_message = NULL;
> >       }
> >
> >       line6_start_listen(line6);
> > @@ -522,6 +527,163 @@ static void line6_get_interval(struct usb_line6 *line6)
> >       }
> >  }
> >
> > +
> > +/* Enable buffering of incoming messages, flush the buffer */
> > +static int line6_hwdep_open(struct snd_hwdep *hw, struct file *file)
> > +{
> > +     struct usb_line6 *line6 = hw->private_data;
> > +
> > +     /* NOTE: hwdep already provides atomicity (exclusive == true), but for
> > +      * sure... */
> > +     if (test_and_set_bit(0, &line6->buffer_circular.active))
> > +             return -EBUSY;
> > +
> > +     line6->buffer_circular.head = 0;
> > +     line6->buffer_circular.tail = 0;
> > +     sema_init(&line6->buffer_circular.sem, 0);
>
> Why do you use semaphore, not mutex?  And initializing at this
> point...?  It looks racy.
>

I can move it to hwdep_init, but here it should be fine too, since
hwdep_open() is serialized by hwdep layer.

Semaphore is used because it also uses as counter for hwdep_read,
which otherwise I'd have to do manually. I could rewrite this to use
waitqueues, but when the counter is added to that, I end up basically
with semaphore reimplementation... Do you see this as a big issue?


>
>
> > +     line6->buffer_circular.active = 1;
>
> Looks superfluous, done in the above?
>

This is here to just drop the USB packets if there's no active reader.
To save some CPU time basically...

>
>
> > +     line6->buffer_circular.data =
> > +             kmalloc(LINE6_MESSAGE_MAXLEN * LINE6_MESSAGE_MAXCOUNT, GFP_KERNEL);
> > +     if (!line6->buffer_circular.data) {
> > +             return -ENOMEM;
> > +     }
> > +     line6->buffer_circular.data_len =
> > +             kmalloc(sizeof(*line6->buffer_circular.data_len) *
> > +                     LINE6_MESSAGE_MAXCOUNT, GFP_KERNEL);
> > +     if (!line6->buffer_circular.data_len) {
> > +             kfree(line6->buffer_circular.data);
> > +             return -ENOMEM;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +/* Stop buffering */
> > +static int line6_hwdep_release(struct snd_hwdep *hw, struct file *file)
> > +{
> > +     struct usb_line6 *line6 = hw->private_data;
> > +
> > +     /* By this time, no readers are waiting, we can safely recreate the
> > +      * semaphore at next open. */
> > +     line6->buffer_circular.active = 0;
> > +
> > +     kfree(line6->buffer_circular.data);
> > +     kfree(line6->buffer_circular.data_len);
> > +     return 0;
> > +}
> > +
> > +/* Read from circular buffer, return to user */
> > +static long
> > +line6_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
> > +                                     loff_t *offset)
> > +{
> > +     struct usb_line6 *line6 = hwdep->private_data;
> > +     unsigned long tail;
>
> No need to use long for FIFO index.
>

See below.


>
> > +     if (down_interruptible(&line6->buffer_circular.sem)) {
> > +             return -ERESTARTSYS;
> > +     }
> > +     /* There must an item now in the buffer... */
> > +
> > +     tail = line6->buffer_circular.tail;
> > +
> > +     if (line6->buffer_circular.data_len[tail] > count) {
> > +             /* Buffer too small; allow re-read of the current item... */
> > +             up(&line6->buffer_circular.sem);
> > +             return -EINVAL;
> > +     }
> > +
> > +     if (copy_to_user(buf,
> > +             &line6->buffer_circular.data[tail * LINE6_MESSAGE_MAXLEN],
> > +             line6->buffer_circular.data_len[tail])
> > +     ) {
> > +             rv = -EFAULT;
> > +             goto end;
> > +     } else {
> > +             rv = line6->buffer_circular.data_len[tail];
> > +     }
> > +
> > +     smp_store_release(&line6->buffer_circular.tail,
> > +                             (tail + 1) & (LINE6_MESSAGE_MAXCOUNT - 1));
> > +
> > +     return 0;
> > +}
> > +
> > +/* Write directly (no buffering) to device by user*/
> > +static long
> > +line6_hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count,
> > +                                     loff_t *offset)
> > +{
> > +     struct usb_line6 *line6 = hwdep->private_data;
> > +     int rv;
> > +     char *data_copy;
> > +
> > +     data_copy = kmalloc(count, GFP_ATOMIC);
>
> No need to use GFP_ATOMIC in this context.
> Also, you should add the sanity check of the given size.  User may
> pass any size of the write.
>
> > +     if (!data_copy)
> > +             return -ENOMEM;
> > +
> > +     if (copy_from_user(data_copy, data, count))
> > +             rv = -EFAULT;
>
> Maybe easier to use memdup_user() helper.
>
> > +     else
> > +             rv = line6_send_raw_message(line6, data_copy, count);
> > +
> > +     kfree(data_copy);
> > +     return rv;
> > +}
> > +
> > +static const struct snd_hwdep_ops hwdep_ops = {
> > +     .open    = line6_hwdep_open,
> > +     .release = line6_hwdep_release,
> > +     .read    = line6_hwdep_read,
> > +     .write   = line6_hwdep_write,
> > +};
> > +
> > +/* Insert into circular buffer */
> > +static void line6_hwdep_push_message(struct usb_line6 *line6)
> > +{
> > +     unsigned long head = line6->buffer_circular.head;
> > +     /* The spin_unlock() and next spin_lock() provide needed ordering. */
> > +     unsigned long tail = ACCESS_ONCE(line6->buffer_circular.tail);
> > +
> > +     if (!line6->buffer_circular.active)
> > +             return;
> > +
> > +     if (CIRC_SPACE(head, tail, LINE6_MESSAGE_MAXCOUNT) >= 1) {
> > +             unsigned char *item = &line6->buffer_circular.data[
> > +                     head * LINE6_MESSAGE_MAXLEN];
> > +             memcpy(item, line6->buffer_message, line6->message_length);
> > +             line6->buffer_circular.data_len[head] = line6->message_length;
> > +
> > +             smp_store_release(&line6->buffer_circular.head,
> > +                               (head + 1) & (LINE6_MESSAGE_MAXCOUNT - 1));
> > +             up(&line6->buffer_circular.sem);
> > +     }
>
> Hmm...  this kind of a simple FIFO can be seen in anywhere in the
> kernel code, and I'm sure that you can find an easier way to implement
> it.  The whole code looks a bit scary as it being home-brewed.
>

This code is based on Documentation/circular-buffers.txt, except for
the semaphore magic.


> Also, the blocking read/write control isn't usually done by a
> semaphore.  Then you can handle the interrupt there.
>
>

I actually wonder why, semaphores seemed perfect for this... Do you
have some hints?

-- 
Andrej

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH 12/15] ALSA: line6: Give up on the lock while URBs are released.
  2016-08-12  8:45   ` Takashi Iwai
@ 2016-08-12 11:14     ` Andrej Kruták
  2016-08-12 12:04       ` Takashi Iwai
  0 siblings, 1 reply; 81+ messages in thread
From: Andrej Kruták @ 2016-08-12 11:14 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel

On Fri, Aug 12, 2016 at 10:45 AM, Takashi Iwai <tiwai@suse.de> wrote:
> On Thu, 11 Aug 2016 21:02:24 +0200,
> Andrej Krutak wrote:
>>
>> Done, because line6_stream_stop() locks and calls line6_unlink_audio_urbs(),
>> which in turn invokes audio_out_callback(), which tries to lock 2nd time.
>
> Ditto, write the patch right from the beginning :)
>
>

This actually fixes a lock-bug in the code, so I think I'll send it
separately (like patch 15). Will think about it...

-- 
Andrej

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH 09/15] ALSA: line6: Add hwdep interface to access the POD control messages
  2016-08-12 11:10     ` Andrej Kruták
@ 2016-08-12 12:03       ` Takashi Iwai
  2016-08-12 12:15         ` Andrej Kruták
  0 siblings, 1 reply; 81+ messages in thread
From: Takashi Iwai @ 2016-08-12 12:03 UTC (permalink / raw)
  To: Andrej Kruták; +Cc: alsa-devel

On Fri, 12 Aug 2016 13:10:37 +0200,
Andrej Kruták wrote:
> > > +static void line6_hwdep_push_message(struct usb_line6 *line6)
> > > +{
> > > +     unsigned long head = line6->buffer_circular.head;
> > > +     /* The spin_unlock() and next spin_lock() provide needed ordering. */
> > > +     unsigned long tail = ACCESS_ONCE(line6->buffer_circular.tail);
> > > +
> > > +     if (!line6->buffer_circular.active)
> > > +             return;
> > > +
> > > +     if (CIRC_SPACE(head, tail, LINE6_MESSAGE_MAXCOUNT) >= 1) {
> > > +             unsigned char *item = &line6->buffer_circular.data[
> > > +                     head * LINE6_MESSAGE_MAXLEN];
> > > +             memcpy(item, line6->buffer_message, line6->message_length);
> > > +             line6->buffer_circular.data_len[head] = line6->message_length;
> > > +
> > > +             smp_store_release(&line6->buffer_circular.head,
> > > +                               (head + 1) & (LINE6_MESSAGE_MAXCOUNT - 1));
> > > +             up(&line6->buffer_circular.sem);
> > > +     }
> >
> > Hmm...  this kind of a simple FIFO can be seen in anywhere in the
> > kernel code, and I'm sure that you can find an easier way to implement
> > it.  The whole code looks a bit scary as it being home-brewed.
> >
> 
> This code is based on Documentation/circular-buffers.txt, except for
> the semaphore magic.

The example there is basically a semi lock-free implementation.  For
your purpose it's an overkill.  This is no severely hot path, thus a
simpler version would make life easier.

> > Also, the blocking read/write control isn't usually done by a
> > semaphore.  Then you can handle the interrupt there.
> >
> >
> 
> I actually wonder why, semaphores seemed perfect for this... Do you
> have some hints?

Assume you want to interrupt the user-space app while it's being
blocked by the semaphore.  With your code, you can't.


Takashi
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH 12/15] ALSA: line6: Give up on the lock while URBs are released.
  2016-08-12 11:14     ` Andrej Kruták
@ 2016-08-12 12:04       ` Takashi Iwai
  0 siblings, 0 replies; 81+ messages in thread
From: Takashi Iwai @ 2016-08-12 12:04 UTC (permalink / raw)
  To: Andrej Kruták; +Cc: alsa-devel

On Fri, 12 Aug 2016 13:14:35 +0200,
Andrej Kruták wrote:
> 
> On Fri, Aug 12, 2016 at 10:45 AM, Takashi Iwai <tiwai@suse.de> wrote:
> > On Thu, 11 Aug 2016 21:02:24 +0200,
> > Andrej Krutak wrote:
> >>
> >> Done, because line6_stream_stop() locks and calls line6_unlink_audio_urbs(),
> >> which in turn invokes audio_out_callback(), which tries to lock 2nd time.
> >
> > Ditto, write the patch right from the beginning :)
> >
> >
> 
> This actually fixes a lock-bug in the code, so I think I'll send it
> separately (like patch 15). Will think about it...

Ah, right, this is the bug in the upstream code.  Then you should
submit it individually, instead of burying in a patchset.


Takashi
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH 09/15] ALSA: line6: Add hwdep interface to access the POD control messages
  2016-08-12 12:03       ` Takashi Iwai
@ 2016-08-12 12:15         ` Andrej Kruták
  2016-08-12 12:30           ` Takashi Iwai
  0 siblings, 1 reply; 81+ messages in thread
From: Andrej Kruták @ 2016-08-12 12:15 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel

On Fri, Aug 12, 2016 at 2:03 PM, Takashi Iwai <tiwai@suse.de> wrote:
> On Fri, 12 Aug 2016 13:10:37 +0200,
> Andrej Kruták wrote:
>> > > +static void line6_hwdep_push_message(struct usb_line6 *line6)
>> > > +{
>> > > +     unsigned long head = line6->buffer_circular.head;
>> > > +     /* The spin_unlock() and next spin_lock() provide needed ordering. */
>> > > +     unsigned long tail = ACCESS_ONCE(line6->buffer_circular.tail);
>> > > +
>> > > +     if (!line6->buffer_circular.active)
>> > > +             return;
>> > > +
>> > > +     if (CIRC_SPACE(head, tail, LINE6_MESSAGE_MAXCOUNT) >= 1) {
>> > > +             unsigned char *item = &line6->buffer_circular.data[
>> > > +                     head * LINE6_MESSAGE_MAXLEN];
>> > > +             memcpy(item, line6->buffer_message, line6->message_length);
>> > > +             line6->buffer_circular.data_len[head] = line6->message_length;
>> > > +
>> > > +             smp_store_release(&line6->buffer_circular.head,
>> > > +                               (head + 1) & (LINE6_MESSAGE_MAXCOUNT - 1));
>> > > +             up(&line6->buffer_circular.sem);
>> > > +     }
>> >
>> > Hmm...  this kind of a simple FIFO can be seen in anywhere in the
>> > kernel code, and I'm sure that you can find an easier way to implement
>> > it.  The whole code looks a bit scary as it being home-brewed.
>> >
>>
>> This code is based on Documentation/circular-buffers.txt, except for
>> the semaphore magic.
>
> The example there is basically a semi lock-free implementation.  For
> your purpose it's an overkill.  This is no severely hot path, thus a
> simpler version would make life easier.
>

Fair enough, I'll spinlock-ize it then.


>> > Also, the blocking read/write control isn't usually done by a
>> > semaphore.  Then you can handle the interrupt there.
>> >
>> >
>>
>> I actually wonder why, semaphores seemed perfect for this... Do you
>> have some hints?
>
> Assume you want to interrupt the user-space app while it's being
> blocked by the semaphore.  With your code, you can't.
>
>

You can - down_interruptible() is there for this exact reason. As
said, semaphore is basically wait-queue + counter, so there's no real
reason not to do this IMO.

But in the end - if you have some nice example of FIFO buffer in some
simple driver, I have no problem using a more already-proven/standard
way :-)


-- 
Andrej
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH 09/15] ALSA: line6: Add hwdep interface to access the POD control messages
  2016-08-12 12:15         ` Andrej Kruták
@ 2016-08-12 12:30           ` Takashi Iwai
  2016-08-12 16:43             ` Andrej Kruták
  0 siblings, 1 reply; 81+ messages in thread
From: Takashi Iwai @ 2016-08-12 12:30 UTC (permalink / raw)
  To: Andrej Kruták; +Cc: alsa-devel

On Fri, 12 Aug 2016 14:15:16 +0200,
Andrej Kruták wrote:
> 
> On Fri, Aug 12, 2016 at 2:03 PM, Takashi Iwai <tiwai@suse.de> wrote:
> > On Fri, 12 Aug 2016 13:10:37 +0200,
> > Andrej Kruták wrote:
> >> > > +static void line6_hwdep_push_message(struct usb_line6 *line6)
> >> > > +{
> >> > > +     unsigned long head = line6->buffer_circular.head;
> >> > > +     /* The spin_unlock() and next spin_lock() provide needed ordering. */
> >> > > +     unsigned long tail = ACCESS_ONCE(line6->buffer_circular.tail);
> >> > > +
> >> > > +     if (!line6->buffer_circular.active)
> >> > > +             return;
> >> > > +
> >> > > +     if (CIRC_SPACE(head, tail, LINE6_MESSAGE_MAXCOUNT) >= 1) {
> >> > > +             unsigned char *item = &line6->buffer_circular.data[
> >> > > +                     head * LINE6_MESSAGE_MAXLEN];
> >> > > +             memcpy(item, line6->buffer_message, line6->message_length);
> >> > > +             line6->buffer_circular.data_len[head] = line6->message_length;
> >> > > +
> >> > > +             smp_store_release(&line6->buffer_circular.head,
> >> > > +                               (head + 1) & (LINE6_MESSAGE_MAXCOUNT - 1));
> >> > > +             up(&line6->buffer_circular.sem);
> >> > > +     }
> >> >
> >> > Hmm...  this kind of a simple FIFO can be seen in anywhere in the
> >> > kernel code, and I'm sure that you can find an easier way to implement
> >> > it.  The whole code looks a bit scary as it being home-brewed.
> >> >
> >>
> >> This code is based on Documentation/circular-buffers.txt, except for
> >> the semaphore magic.
> >
> > The example there is basically a semi lock-free implementation.  For
> > your purpose it's an overkill.  This is no severely hot path, thus a
> > simpler version would make life easier.
> >
> 
> Fair enough, I'll spinlock-ize it then.
> 
> 
> >> > Also, the blocking read/write control isn't usually done by a
> >> > semaphore.  Then you can handle the interrupt there.
> >> >
> >> >
> >>
> >> I actually wonder why, semaphores seemed perfect for this... Do you
> >> have some hints?
> >
> > Assume you want to interrupt the user-space app while it's being
> > blocked by the semaphore.  With your code, you can't.
> >
> >
> 
> You can - down_interruptible() is there for this exact reason.

There is another blocking path: you keep semaphore down after
line6_hwdep_read() until line6_hwdep_push_message().  What happens if
user-space interrupts during that, and line6_hwdep_push_message() is
delayed or stall by some reason?

> As
> said, semaphore is basically wait-queue + counter, so there's no real
> reason not to do this IMO.
> 
> But in the end - if you have some nice example of FIFO buffer in some
> simple driver, I have no problem using a more already-proven/standard
> way :-)

Just use the normal waitqueue and schedule or wait_event() or its
variant.


Takashi
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH 09/15] ALSA: line6: Add hwdep interface to access the POD control messages
  2016-08-12 12:30           ` Takashi Iwai
@ 2016-08-12 16:43             ` Andrej Kruták
  2016-08-12 20:01               ` Takashi Iwai
  0 siblings, 1 reply; 81+ messages in thread
From: Andrej Kruták @ 2016-08-12 16:43 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel

On Fri, Aug 12, 2016 at 2:30 PM, Takashi Iwai <tiwai@suse.de> wrote:
> On Fri, 12 Aug 2016 14:15:16 +0200,
>>
>> >> > Also, the blocking read/write control isn't usually done by a
>> >> > semaphore.  Then you can handle the interrupt there.
>> >> >
>> >> >
>> >>
>> >> I actually wonder why, semaphores seemed perfect for this... Do you
>> >> have some hints?
>> >
>> > Assume you want to interrupt the user-space app while it's being
>> > blocked by the semaphore.  With your code, you can't.
>> >
>> >
>>
>> You can - down_interruptible() is there for this exact reason.
>
> There is another blocking path: you keep semaphore down after
> line6_hwdep_read() until line6_hwdep_push_message().  What happens if
> user-space interrupts during that, and line6_hwdep_push_message() is
> delayed or stall by some reason?
>

Actually, I think I don't see what's the "another path" here, could
you please elaborate one more bit? I just want to make sure to not
reimplement the same race using waitqueue...

What's the point then? line6_hwdep_push_message() could get not
scheduled for some while. So until it calls up(), line6_hwdep_read()
will block on down_interruptible(), or until signal (in which case
user gets -ERESTARTSYS). After up() is called, there are data in
buffer... If line6_hwdep_read() returns after interrupt, no problem -
the buffer will just continue to be filled and semaphore will be
up()'d while there's free buffer space. Or until the device is
closed...

If I do the same via waitqueue, I will have the same problems, no?
Maybe if you could post the steps where you see the race...

At the same time, looking at __down_common(), it just does the
standard waitqueue stuff (TASK_INTERRUPTIBLE + schedule()) (+counter
in down())... So do you have some other race in mind? I'm running in
circles, so surely you must :-)


Sorry if I sound like a moron... and thanks for you time!

-- 
Andrej

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH 09/15] ALSA: line6: Add hwdep interface to access the POD control messages
  2016-08-12 16:43             ` Andrej Kruták
@ 2016-08-12 20:01               ` Takashi Iwai
  2016-08-14  7:59                 ` Andrej Kruták
  0 siblings, 1 reply; 81+ messages in thread
From: Takashi Iwai @ 2016-08-12 20:01 UTC (permalink / raw)
  To: Andrej Kruták; +Cc: alsa-devel

On Fri, 12 Aug 2016 18:43:30 +0200,
Andrej Kruták wrote:
> 
> On Fri, Aug 12, 2016 at 2:30 PM, Takashi Iwai <tiwai@suse.de> wrote:
> > On Fri, 12 Aug 2016 14:15:16 +0200,
> >>
> >> >> > Also, the blocking read/write control isn't usually done by a
> >> >> > semaphore.  Then you can handle the interrupt there.
> >> >> >
> >> >> >
> >> >>
> >> >> I actually wonder why, semaphores seemed perfect for this... Do you
> >> >> have some hints?
> >> >
> >> > Assume you want to interrupt the user-space app while it's being
> >> > blocked by the semaphore.  With your code, you can't.
> >> >
> >> >
> >>
> >> You can - down_interruptible() is there for this exact reason.
> >
> > There is another blocking path: you keep semaphore down after
> > line6_hwdep_read() until line6_hwdep_push_message().  What happens if
> > user-space interrupts during that, and line6_hwdep_push_message() is
> > delayed or stall by some reason?
> >
> 
> Actually, I think I don't see what's the "another path" here, could
> you please elaborate one more bit? I just want to make sure to not
> reimplement the same race using waitqueue...
> 
> What's the point then? line6_hwdep_push_message() could get not
> scheduled for some while. So until it calls up(), line6_hwdep_read()
> will block on down_interruptible(), or until signal (in which case
> user gets -ERESTARTSYS). After up() is called, there are data in
> buffer...

Well, what happens if user aborts before up() is called in
line6_hwdep_push_message()?  Now the driver calls close, and it frees
the memory.  What if, at the very same time,
line6_hwdep_push_message() is triggered?

> If line6_hwdep_read() returns after interrupt, no problem -
> the buffer will just continue to be filled and semaphore will be
> up()'d while there's free buffer space. Or until the device is
> closed...

Or, what if line6_hwdep_push_message() is triggered twice or more
before line6_hwdep_read() is called?  It will call up() twice or
more.  Then at this point, you call line6_hwdep_read() concurrently
from two threads...  How do they protect against each other?

> If I do the same via waitqueue, I will have the same problems, no?
> Maybe if you could post the steps where you see the race...

In your code, it's not clear that you're protecting from what.
A simple lock+wait loop shows it more easily, at least.


Takashi

> At the same time, looking at __down_common(), it just does the
> standard waitqueue stuff (TASK_INTERRUPTIBLE + schedule()) (+counter
> in down())... So do you have some other race in mind? I'm running in
> circles, so surely you must :-)
> 
> 
> Sorry if I sound like a moron... and thanks for you time!
> 
> -- 
> Andrej
> 
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH 09/15] ALSA: line6: Add hwdep interface to access the POD control messages
  2016-08-12 20:01               ` Takashi Iwai
@ 2016-08-14  7:59                 ` Andrej Kruták
  0 siblings, 0 replies; 81+ messages in thread
From: Andrej Kruták @ 2016-08-14  7:59 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel

On Fri, Aug 12, 2016 at 10:01 PM, Takashi Iwai <tiwai@suse.de> wrote:
> On Fri, 12 Aug 2016 18:43:30 +0200,
> Andrej Kruták wrote:
>>
>> On Fri, Aug 12, 2016 at 2:30 PM, Takashi Iwai <tiwai@suse.de> wrote:
>> > On Fri, 12 Aug 2016 14:15:16 +0200,
>> >>
>> >> >> > Also, the blocking read/write control isn't usually done by a
>> >> >> > semaphore.  Then you can handle the interrupt there.
>> >> >> >
>> >> >> >
>> >> >>
>> >> >> I actually wonder why, semaphores seemed perfect for this... Do you
>> >> >> have some hints?
>> >> >
>> >> > Assume you want to interrupt the user-space app while it's being
>> >> > blocked by the semaphore.  With your code, you can't.
>> >> >
>> >> >
>> >>
>> >> You can - down_interruptible() is there for this exact reason.
>> >
>> > There is another blocking path: you keep semaphore down after
>> > line6_hwdep_read() until line6_hwdep_push_message().  What happens if
>> > user-space interrupts during that, and line6_hwdep_push_message() is
>> > delayed or stall by some reason?
>> >
>>
>> Actually, I think I don't see what's the "another path" here, could
>> you please elaborate one more bit? I just want to make sure to not
>> reimplement the same race using waitqueue...
>>
>> What's the point then? line6_hwdep_push_message() could get not
>> scheduled for some while. So until it calls up(), line6_hwdep_read()
>> will block on down_interruptible(), or until signal (in which case
>> user gets -ERESTARTSYS). After up() is called, there are data in
>> buffer...
>
> Well, what happens if user aborts before up() is called in
> line6_hwdep_push_message()?  Now the driver calls close, and it frees
> the memory.  What if, at the very same time,
> line6_hwdep_push_message() is triggered?
>

Right, the 'active' flag is cleary not a good lock...


>> If line6_hwdep_read() returns after interrupt, no problem -
>> the buffer will just continue to be filled and semaphore will be
>> up()'d while there's free buffer space. Or until the device is
>> closed...
>
> Or, what if line6_hwdep_push_message() is triggered twice or more
> before line6_hwdep_read() is called?  It will call up() twice or
> more.  Then at this point, you call line6_hwdep_read() concurrently
> from two threads...  How do they protect against each other?
>

There's read_lock, unfortunately after "fixing" the sleep-in-atomic in
line6_hwdep_read(), it's broken now...


>> If I do the same via waitqueue, I will have the same problems, no?
>> Maybe if you could post the steps where you see the race...
>
> In your code, it's not clear that you're protecting from what.
> A simple lock+wait loop shows it more easily, at least.
>

Okay, I'll try to rethink/rework it, simplify the code, and get back.

Thanks!


-- 
Andrej
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 81+ messages in thread

* [PATCH v2 0/9] Line6 POD X3/X3Live suport
  2016-08-11 19:02 [PATCH 00/15] Line6 POD X3/X3Live suport Andrej Krutak
                   ` (15 preceding siblings ...)
  2016-08-12  8:14 ` [PATCH 00/15] Line6 POD X3/X3Live suport Takashi Iwai
@ 2016-08-18 22:20 ` Andrej Krutak
  2016-08-18 22:20   ` [PATCH v2 1/9] ALSA: line6: Make driver configuration more generic Andrej Krutak
                     ` (8 more replies)
  2016-09-16  9:12 ` [PATCH v3 00/12] Line6 POD X3/X3Live suport Andrej Krutak
  2016-09-18 18:59 ` [PATCH v4 00/12] Line6 POD X3/X3Live suport Andrej Krutak
  18 siblings, 9 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-08-18 22:20 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

Hi,


I reworked the patches acc. to Takashi's feedback, I think I was able
to fix all the "objections". Two of the previous patches were sent in a
separate batch.

About first patch below - I just added some more commit notes there. I tried
to split it, but it's kind of interconnected and in the end, they are mostly
sed-like operations.. If it's a no-go, I can do it, but I'd prefer not to :-)

Some commits were merged to "Add hwdep IF" commit, as suggested. Also,
I dropped manual implementation of the FIFO buffer in favor of KFIFO.
In addition, mutex is used for read() - as required by the kfifo lib. The
code now looks a lot less scary, I'd say...


Andrej Krutak (9):
  ALSA: line6: Make driver configuration more generic.
  ALSA: line6: Add LINE6_CAP_IN_NEEDS_OUT, a void playback stream during
    capture
  ALSA: line6: Distinguish device init (ctrl EP) and MIDI data transfer
    (int EP)
  ALSA: line6: Add support for POD X3
  ALSA: line6: Add support for POD X3 Live (only USB ID differs from POD
    X3)
  ALSA: line6: Allow bulk endpoints instead of interrupt endpoints
  ALSA: line6: Allow processing of raw incoming messages
  ALSA: line6: Cleanup initialization
  ALSA: line6: Add hwdep interface to access the POD control messages

 include/uapi/sound/asound.h |   3 +-
 sound/usb/line6/Kconfig     |   4 +-
 sound/usb/line6/capture.c   |  50 +++++---
 sound/usb/line6/driver.c    | 269 +++++++++++++++++++++++++++++++++------
 sound/usb/line6/driver.h    |  58 +++++++--
 sound/usb/line6/midi.c      |   2 +-
 sound/usb/line6/pcm.c       |  84 +++++++++----
 sound/usb/line6/pcm.h       |  19 +--
 sound/usb/line6/playback.c  |  37 ++++--
 sound/usb/line6/pod.c       |  12 +-
 sound/usb/line6/podhd.c     | 300 ++++++++++++++++++++++++++++++++++++++++----
 sound/usb/line6/toneport.c  |   6 +-
 sound/usb/line6/variax.c    |   6 +-
 13 files changed, 704 insertions(+), 146 deletions(-)

-- 
1.9.1

^ permalink raw reply	[flat|nested] 81+ messages in thread

* [PATCH v2 1/9] ALSA: line6: Make driver configuration more generic.
  2016-08-18 22:20 ` [PATCH v2 0/9] " Andrej Krutak
@ 2016-08-18 22:20   ` Andrej Krutak
  2016-08-24 14:50     ` Takashi Iwai
  2016-08-18 22:20   ` [PATCH v2 2/9] ALSA: line6: Add LINE6_CAP_IN_NEEDS_OUT, a void playback stream during capture Andrej Krutak
                     ` (7 subsequent siblings)
  8 siblings, 1 reply; 81+ messages in thread
From: Andrej Krutak @ 2016-08-18 22:20 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

The main reasons are different settings for USB low/high speed and possible
different channel counts for in/out; required by POD X3.

This consists of two related parts:

Support for high-speed USB:
* USB_INTERVALS_PER_SECOND -> LOW/HIGH settings
  (high needs 8000, instead of 1000)
* LINE6_ISO_BUFFERS -> iso_buffers (count of iso buffers depends on
  USB speed, 2 is not enough for high speed)

Support for assymetrical in/out configurations:
* bytes_per_frame -> bytes_per_channel
* max_packet_size -> max_packet_size_in/out

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/capture.c  | 45 ++++++++++++++++++++++++++++++--------------
 sound/usb/line6/driver.c   | 15 ++++++++++-----
 sound/usb/line6/driver.h   | 19 +++++++++++++++----
 sound/usb/line6/pcm.c      | 47 ++++++++++++++++++++++++++++------------------
 sound/usb/line6/pcm.h      | 15 +++++++--------
 sound/usb/line6/playback.c | 37 ++++++++++++++++++++++++------------
 sound/usb/line6/pod.c      |  3 +--
 sound/usb/line6/podhd.c    |  4 +---
 sound/usb/line6/toneport.c |  2 +-
 9 files changed, 120 insertions(+), 67 deletions(-)

diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c
index f518fbb..91d1562 100644
--- a/sound/usb/line6/capture.c
+++ b/sound/usb/line6/capture.c
@@ -29,10 +29,10 @@ static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm)
 	int ret;
 	struct urb *urb_in;
 
-	index =
-	    find_first_zero_bit(&line6pcm->in.active_urbs, LINE6_ISO_BUFFERS);
+	index = find_first_zero_bit(
+		&line6pcm->in.active_urbs, line6pcm->line6->iso_buffers);
 
-	if (index < 0 || index >= LINE6_ISO_BUFFERS) {
+	if (index < 0 || index >= line6pcm->line6->iso_buffers) {
 		dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
 		return -EINVAL;
 	}
@@ -44,13 +44,13 @@ static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm)
 		struct usb_iso_packet_descriptor *fin =
 		    &urb_in->iso_frame_desc[i];
 		fin->offset = urb_size;
-		fin->length = line6pcm->max_packet_size;
-		urb_size += line6pcm->max_packet_size;
+		fin->length = line6pcm->max_packet_size_in;
+		urb_size += line6pcm->max_packet_size_in;
 	}
 
 	urb_in->transfer_buffer =
 	    line6pcm->in.buffer +
-	    index * LINE6_ISO_PACKETS * line6pcm->max_packet_size;
+	    index * LINE6_ISO_PACKETS * line6pcm->max_packet_size_in;
 	urb_in->transfer_buffer_length = urb_size;
 	urb_in->context = line6pcm;
 
@@ -73,7 +73,7 @@ int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm)
 {
 	int ret = 0, i;
 
-	for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
+	for (i = 0; i < line6pcm->line6->iso_buffers; ++i) {
 		ret = submit_audio_in_urb(line6pcm);
 		if (ret < 0)
 			break;
@@ -90,7 +90,9 @@ void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize)
 	struct snd_pcm_substream *substream =
 	    get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE);
 	struct snd_pcm_runtime *runtime = substream->runtime;
-	const int bytes_per_frame = line6pcm->properties->bytes_per_frame;
+	const int bytes_per_frame =
+		line6pcm->properties->bytes_per_channel *
+		line6pcm->properties->capture_hw.channels_max;
 	int frames = fsize / bytes_per_frame;
 
 	if (runtime == NULL)
@@ -154,7 +156,7 @@ static void audio_in_callback(struct urb *urb)
 	line6pcm->in.last_frame = urb->start_frame;
 
 	/* find index of URB */
-	for (index = 0; index < LINE6_ISO_BUFFERS; ++index)
+	for (index = 0; index < line6pcm->line6->iso_buffers; ++index)
 		if (urb == line6pcm->in.urbs[index])
 			break;
 
@@ -173,17 +175,27 @@ static void audio_in_callback(struct urb *urb)
 		fbuf = urb->transfer_buffer + fin->offset;
 		fsize = fin->actual_length;
 
-		if (fsize > line6pcm->max_packet_size) {
+		if (fsize > line6pcm->max_packet_size_in) {
 			dev_err(line6pcm->line6->ifcdev,
 				"driver and/or device bug: packet too large (%d > %d)\n",
-				fsize, line6pcm->max_packet_size);
+				fsize, line6pcm->max_packet_size_in);
 		}
 
 		length += fsize;
 
-		/* the following assumes LINE6_ISO_PACKETS == 1: */
+		BUILD_BUG_ON_MSG(LINE6_ISO_PACKETS != 1,
+			"The following code assumes LINE6_ISO_PACKETS == 1");
+		/* TODO:
+		 * Also, if iso_buffers != 2, the prev frame is almost random at
+		 * playback side.
+		 * This needs to be redesigned. It should be "stable", but we may
+		 * experience sync problems on such high-speed configs.
+		 */
+
 		line6pcm->prev_fbuf = fbuf;
-		line6pcm->prev_fsize = fsize;
+		line6pcm->prev_fsize = fsize /
+			(line6pcm->properties->bytes_per_channel *
+			line6pcm->properties->capture_hw.channels_max);
 
 		if (!test_bit(LINE6_STREAM_IMPULSE, &line6pcm->in.running) &&
 		    test_bit(LINE6_STREAM_PCM, &line6pcm->in.running) &&
@@ -247,8 +259,13 @@ int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm)
 	struct usb_line6 *line6 = line6pcm->line6;
 	int i;
 
+	line6pcm->in.urbs = kzalloc(
+		sizeof(struct urb *) * line6->iso_buffers, GFP_KERNEL);
+	if (line6pcm->in.urbs == NULL)
+		return -ENOMEM;
+
 	/* create audio URBs and fill in constant values: */
-	for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
+	for (i = 0; i < line6->iso_buffers; ++i) {
 		struct urb *urb;
 
 		/* URB for audio in: */
diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 81b7da8..efeb16a8 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -462,13 +462,17 @@ static void line6_destruct(struct snd_card *card)
 static void line6_get_interval(struct usb_line6 *line6)
 {
 	struct usb_device *usbdev = line6->usbdev;
-	struct usb_host_endpoint *ep;
-	unsigned pipe = usb_rcvintpipe(usbdev, line6->properties->ep_ctrl_r);
-	unsigned epnum = usb_pipeendpoint(pipe);
-
-	ep = usbdev->ep_in[epnum];
+	struct usb_host_endpoint *ep = usbdev->ep_in[line6->properties->ep_ctrl_r];
 	if (ep) {
 		line6->interval = ep->desc.bInterval;
+		if (usbdev->speed == USB_SPEED_LOW) {
+			line6->intervals_per_second = USB_LOW_INTERVALS_PER_SECOND;
+			line6->iso_buffers = USB_LOW_ISO_BUFFERS;
+		} else {
+			line6->intervals_per_second = USB_HIGH_INTERVALS_PER_SECOND;
+			line6->iso_buffers = USB_HIGH_ISO_BUFFERS;
+		}
+
 		line6->max_packet_size = le16_to_cpu(ep->desc.wMaxPacketSize);
 	} else {
 		dev_err(line6->ifcdev,
@@ -558,6 +562,7 @@ int line6_probe(struct usb_interface *interface,
 	/* query interface number */
 	interface_number = interface->cur_altsetting->desc.bInterfaceNumber;
 
+	/* TODO reserves the bus bandwidth even without actual transfer */
 	ret = usb_set_interface(usbdev, interface_number,
 				properties->altsetting);
 	if (ret < 0) {
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index 7da643e..2d32139 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -18,14 +18,20 @@
 
 #include "midi.h"
 
-#define USB_INTERVALS_PER_SECOND 1000
+/* USB 1.1 speed configuration */
+#define USB_LOW_INTERVALS_PER_SECOND 1000
+#define USB_LOW_ISO_BUFFERS 2
+
+/* USB 2.0+ speed configuration */
+#define USB_HIGH_INTERVALS_PER_SECOND 8000
+#define USB_HIGH_ISO_BUFFERS 16
 
 /* Fallback USB interval and max packet size values */
 #define LINE6_FALLBACK_INTERVAL 10
 #define LINE6_FALLBACK_MAXPACKETSIZE 16
 
 #define LINE6_TIMEOUT 1
-#define LINE6_BUFSIZE_LISTEN 32
+#define LINE6_BUFSIZE_LISTEN 64
 #define LINE6_MESSAGE_MAXLEN 256
 
 /*
@@ -109,10 +115,15 @@ struct usb_line6 {
 	/* Properties */
 	const struct line6_properties *properties;
 
-	/* Interval (ms) */
+	/* Interval for data USB packets */
 	int interval;
+	/* ...for isochronous transfers framing */
+	int intervals_per_second;
+
+	/* Number of isochronous URBs used for frame transfers */
+	int iso_buffers;
 
-	/* Maximum size of USB packet */
+	/* Maximum size of data USB packet */
 	int max_packet_size;
 
 	/* Device representing the USB interface */
diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c
index 41aa335..e1913d3 100644
--- a/sound/usb/line6/pcm.c
+++ b/sound/usb/line6/pcm.c
@@ -104,7 +104,7 @@ static void line6_unlink_audio_urbs(struct snd_line6_pcm *line6pcm,
 {
 	int i;
 
-	for (i = 0; i < LINE6_ISO_BUFFERS; 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]);
@@ -124,7 +124,7 @@ static void line6_wait_clear_audio_urbs(struct snd_line6_pcm *line6pcm,
 
 	do {
 		alive = 0;
-		for (i = 0; i < LINE6_ISO_BUFFERS; i++) {
+		for (i = 0; i < line6pcm->line6->iso_buffers; i++) {
 			if (test_bit(i, &pcms->active_urbs))
 				alive++;
 		}
@@ -146,15 +146,20 @@ get_stream(struct snd_line6_pcm *line6pcm, int direction)
 }
 
 /* allocate a buffer if not opened yet;
- * call this in line6pcm.state_change mutex
+ * call this in line6pcm.state_mutex
  */
 static int line6_buffer_acquire(struct snd_line6_pcm *line6pcm,
-				struct line6_pcm_stream *pstr, int type)
+				struct line6_pcm_stream *pstr, int direction, int type)
 {
+	const int 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(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS *
-				       line6pcm->max_packet_size, GFP_KERNEL);
+		pstr->buffer = kmalloc(line6pcm->line6->iso_buffers *
+							   LINE6_ISO_PACKETS * pkt_size, GFP_KERNEL);
 		if (!pstr->buffer)
 			return -ENOMEM;
 	}
@@ -162,12 +167,11 @@ static int line6_buffer_acquire(struct snd_line6_pcm *line6pcm,
 }
 
 /* free a buffer if all streams are closed;
- * call this in line6pcm.state_change mutex
+ * call this in line6pcm.state_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);
@@ -194,6 +198,7 @@ static int line6_stream_start(struct snd_line6_pcm *line6pcm, int direction,
 		else
 			ret = line6_submit_audio_in_all_urbs(line6pcm);
 	}
+
 	if (ret < 0)
 		clear_bit(type, &pstr->running);
 	spin_unlock_irqrestore(&pstr->lock, flags);
@@ -434,24 +439,30 @@ static struct snd_kcontrol_new line6_controls[] = {
 /*
 	Cleanup the PCM device.
 */
-static void cleanup_urbs(struct line6_pcm_stream *pcms)
+static void cleanup_urbs(struct line6_pcm_stream *pcms, int iso_buffers)
 {
 	int i;
 
-	for (i = 0; i < LINE6_ISO_BUFFERS; i++) {
+	/* Most likely impossible in current code... */
+	if (pcms->urbs == NULL)
+		return;
+
+	for (i = 0; i < iso_buffers; i++) {
 		if (pcms->urbs[i]) {
 			usb_kill_urb(pcms->urbs[i]);
 			usb_free_urb(pcms->urbs[i]);
 		}
 	}
+	kfree(pcms->urbs);
+	pcms->urbs = NULL;
 }
 
 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);
+	cleanup_urbs(&line6pcm->out, line6pcm->line6->iso_buffers);
+	cleanup_urbs(&line6pcm->in, line6pcm->line6->iso_buffers);
 	kfree(line6pcm);
 }
 
@@ -523,12 +534,12 @@ int line6_init_pcm(struct usb_line6 *line6,
 	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));
+	line6pcm->max_packet_size_in =
+		usb_maxpacket(line6->usbdev,
+			usb_rcvisocpipe(line6->usbdev, ep_read), 0);
+	line6pcm->max_packet_size_out =
+		usb_maxpacket(line6->usbdev,
+			usb_sndisocpipe(line6->usbdev, ep_write), 1);
 
 	spin_lock_init(&line6pcm->out.lock);
 	spin_lock_init(&line6pcm->in.lock);
diff --git a/sound/usb/line6/pcm.h b/sound/usb/line6/pcm.h
index 508410a..58d36f9 100644
--- a/sound/usb/line6/pcm.h
+++ b/sound/usb/line6/pcm.h
@@ -20,9 +20,6 @@
 
 #include "driver.h"
 
-/* number of URBs */
-#define LINE6_ISO_BUFFERS	2
-
 /*
 	number of USB frames per URB
 	The Line 6 Windows driver always transmits two frames per packet, but
@@ -31,7 +28,8 @@
 */
 #define LINE6_ISO_PACKETS	1
 
-/* in a "full speed" device (such as the PODxt Pro) this means 1ms */
+/* in a "full speed" device (such as the PODxt Pro) this means 1ms,
+   for "high speed" it's 1/8ms */
 #define LINE6_ISO_INTERVAL	1
 
 #define LINE6_IMPULSE_DEFAULT_PERIOD 100
@@ -85,12 +83,12 @@ enum {
 struct line6_pcm_properties {
 	struct snd_pcm_hardware playback_hw, capture_hw;
 	struct snd_pcm_hw_constraint_ratdens rates;
-	int bytes_per_frame;
+	int bytes_per_channel;
 };
 
 struct line6_pcm_stream {
 	/* allocated URBs */
-	struct urb *urbs[LINE6_ISO_BUFFERS];
+	struct urb **urbs;
 
 	/* Temporary buffer;
 	 * Since the packet size is not known in advance, this buffer is
@@ -157,11 +155,12 @@ struct snd_line6_pcm {
 	/* Previously captured frame (for software monitoring) */
 	unsigned char *prev_fbuf;
 
-	/* Size of previously captured frame (for software monitoring) */
+	/* Size of previously captured frame (for software monitoring/sync) */
 	int prev_fsize;
 
 	/* Maximum size of USB packet */
-	int max_packet_size;
+	int max_packet_size_in;
+	int max_packet_size_out;
 
 	/* PCM playback volume (left and right) */
 	int volume_playback[2];
diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c
index 97ed593..4833c51 100644
--- a/sound/usb/line6/playback.c
+++ b/sound/usb/line6/playback.c
@@ -146,18 +146,20 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
 	int index;
 	int i, urb_size, urb_frames;
 	int ret;
-	const int bytes_per_frame = line6pcm->properties->bytes_per_frame;
+	const int bytes_per_frame =
+		line6pcm->properties->bytes_per_channel *
+		line6pcm->properties->playback_hw.channels_max;
 	const int frame_increment =
 		line6pcm->properties->rates.rats[0].num_min;
 	const int frame_factor =
 		line6pcm->properties->rates.rats[0].den *
-		(USB_INTERVALS_PER_SECOND / LINE6_ISO_INTERVAL);
+		(line6pcm->line6->intervals_per_second / LINE6_ISO_INTERVAL);
 	struct urb *urb_out;
 
-	index =
-	    find_first_zero_bit(&line6pcm->out.active_urbs, LINE6_ISO_BUFFERS);
+	index = find_first_zero_bit(
+		&line6pcm->out.active_urbs, line6pcm->line6->iso_buffers);
 
-	if (index < 0 || index >= LINE6_ISO_BUFFERS) {
+	if (index < 0 || index >= line6pcm->line6->iso_buffers) {
 		dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
 		return -EINVAL;
 	}
@@ -165,6 +167,7 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
 	urb_out = line6pcm->out.urbs[index];
 	urb_size = 0;
 
+	/* TODO: this may not work for LINE6_ISO_PACKETS != 1 */
 	for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
 		/* compute frame size for given sampling rate */
 		int fsize = 0;
@@ -178,9 +181,11 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
 			line6pcm->out.count += frame_increment;
 			n = line6pcm->out.count / frame_factor;
 			line6pcm->out.count -= n * frame_factor;
-			fsize = n * bytes_per_frame;
+			fsize = n;
 		}
 
+		fsize *= bytes_per_frame;
+
 		fout->offset = urb_size;
 		fout->length = fsize;
 		urb_size += fsize;
@@ -195,7 +200,7 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
 	urb_frames = urb_size / bytes_per_frame;
 	urb_out->transfer_buffer =
 	    line6pcm->out.buffer +
-	    index * LINE6_ISO_PACKETS * line6pcm->max_packet_size;
+	    index * LINE6_ISO_PACKETS * line6pcm->max_packet_size_out;
 	urb_out->transfer_buffer_length = urb_size;
 	urb_out->context = line6pcm;
 
@@ -286,7 +291,7 @@ int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm)
 {
 	int ret = 0, i;
 
-	for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
+	for (i = 0; i < line6pcm->line6->iso_buffers; ++i) {
 		ret = submit_audio_out_urb(line6pcm);
 		if (ret < 0)
 			break;
@@ -305,6 +310,9 @@ static void audio_out_callback(struct urb *urb)
 	struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context;
 	struct snd_pcm_substream *substream =
 	    get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK);
+	const int bytes_per_frame =
+		line6pcm->properties->bytes_per_channel *
+		line6pcm->properties->playback_hw.channels_max;
 
 #if USE_CLEAR_BUFFER_WORKAROUND
 	memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);
@@ -313,11 +321,11 @@ static void audio_out_callback(struct urb *urb)
 	line6pcm->out.last_frame = urb->start_frame;
 
 	/* find index of URB */
-	for (index = 0; index < LINE6_ISO_BUFFERS; index++)
+	for (index = 0; index < line6pcm->line6->iso_buffers; index++)
 		if (urb == line6pcm->out.urbs[index])
 			break;
 
-	if (index >= LINE6_ISO_BUFFERS)
+	if (index >= line6pcm->line6->iso_buffers)
 		return;		/* URB has been unlinked asynchronously */
 
 	for (i = 0; i < LINE6_ISO_PACKETS; i++)
@@ -329,7 +337,7 @@ static void audio_out_callback(struct urb *urb)
 		struct snd_pcm_runtime *runtime = substream->runtime;
 
 		line6pcm->out.pos_done +=
-		    length / line6pcm->properties->bytes_per_frame;
+		    length / bytes_per_frame;
 
 		if (line6pcm->out.pos_done >= runtime->buffer_size)
 			line6pcm->out.pos_done -= runtime->buffer_size;
@@ -401,8 +409,13 @@ int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm)
 	struct usb_line6 *line6 = line6pcm->line6;
 	int i;
 
+	line6pcm->out.urbs = kzalloc(
+		sizeof(struct urb *) * line6->iso_buffers, GFP_KERNEL);
+	if (line6pcm->out.urbs == NULL)
+		return -ENOMEM;
+
 	/* create audio URBs and fill in constant values: */
-	for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
+	for (i = 0; i < line6->iso_buffers; ++i) {
 		struct urb *urb;
 
 		/* URB for audio out: */
diff --git a/sound/usb/line6/pod.c b/sound/usb/line6/pod.c
index 45dd348..36e7274 100644
--- a/sound/usb/line6/pod.c
+++ b/sound/usb/line6/pod.c
@@ -83,7 +83,6 @@ struct usb_line6_pod {
 };
 
 #define POD_SYSEX_CODE 3
-#define POD_BYTES_PER_FRAME 6	/* 24bit audio (stereo) */
 
 /* *INDENT-OFF* */
 
@@ -167,7 +166,7 @@ static struct line6_pcm_properties pod_pcm_properties = {
 	.rates = {
 			    .nrats = 1,
 			    .rats = &pod_ratden},
-	.bytes_per_frame = POD_BYTES_PER_FRAME
+	.bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */
 };
 
 static const char pod_version_header[] = {
diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c
index 63dcaef..4fc4789 100644
--- a/sound/usb/line6/podhd.c
+++ b/sound/usb/line6/podhd.c
@@ -25,8 +25,6 @@ enum {
 	LINE6_PODHD500_1,
 };
 
-#define PODHD_BYTES_PER_FRAME 6	/* 24bit audio (stereo) */
-
 static struct snd_ratden podhd_ratden = {
 	.num_min = 48000,
 	.num_max = 48000,
@@ -73,7 +71,7 @@ static struct line6_pcm_properties podhd_pcm_properties = {
 	.rates = {
 			    .nrats = 1,
 			    .rats = &podhd_ratden},
-	.bytes_per_frame = PODHD_BYTES_PER_FRAME
+	.bytes_per_channel = 3 /* 24bit audio (stereo) */
 };
 
 /*
diff --git a/sound/usb/line6/toneport.c b/sound/usb/line6/toneport.c
index 6d4c50c..da76e03 100644
--- a/sound/usb/line6/toneport.c
+++ b/sound/usb/line6/toneport.c
@@ -114,7 +114,7 @@ static struct line6_pcm_properties toneport_pcm_properties = {
 	.rates = {
 			    .nrats = 1,
 			    .rats = &toneport_ratden},
-	.bytes_per_frame = 4
+	.bytes_per_channel = 2
 };
 
 static const struct {
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v2 2/9] ALSA: line6: Add LINE6_CAP_IN_NEEDS_OUT, a void playback stream during capture
  2016-08-18 22:20 ` [PATCH v2 0/9] " Andrej Krutak
  2016-08-18 22:20   ` [PATCH v2 1/9] ALSA: line6: Make driver configuration more generic Andrej Krutak
@ 2016-08-18 22:20   ` Andrej Krutak
  2016-08-24 14:53     ` Takashi Iwai
  2016-08-18 22:20   ` [PATCH v2 3/9] ALSA: line6: Distinguish device init (ctrl EP) and MIDI data transfer (int EP) Andrej Krutak
                     ` (6 subsequent siblings)
  8 siblings, 1 reply; 81+ messages in thread
From: Andrej Krutak @ 2016-08-18 22:20 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

E.g. POD X3 seems to require playback data to be sent to it to generate
capture data. Otherwise the device stalls and doesn't send any more capture
data until it's reset.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/capture.c  |  5 +++++
 sound/usb/line6/driver.h   |  4 +++-
 sound/usb/line6/pcm.c      | 37 ++++++++++++++++++++++++++++---------
 sound/usb/line6/pcm.h      |  4 +++-
 sound/usb/line6/toneport.c |  4 ++--
 5 files changed, 41 insertions(+), 13 deletions(-)

diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c
index 91d1562..37c9e94 100644
--- a/sound/usb/line6/capture.c
+++ b/sound/usb/line6/capture.c
@@ -232,6 +232,8 @@ static int snd_line6_capture_open(struct snd_pcm_substream *substream)
 	if (err < 0)
 		return err;
 
+	line6_pcm_acquire(line6pcm, LINE6_STREAM_CAPTURE_HELPER, false);
+
 	runtime->hw = line6pcm->properties->capture_hw;
 	return 0;
 }
@@ -239,6 +241,9 @@ static int snd_line6_capture_open(struct snd_pcm_substream *substream)
 /* 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;
 }
 
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index 2d32139..69658dc 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -100,8 +100,10 @@ enum {
 	LINE6_CAP_CONTROL =	1 << 0,
 	/* device supports PCM input/output via USB */
 	LINE6_CAP_PCM =		1 << 1,
-	/* device support hardware monitoring */
+	/* device supports hardware monitoring */
 	LINE6_CAP_HWMON =	1 << 2,
+	/* device requires output data when input is read */
+	LINE6_CAP_IN_NEEDS_OUT = 1 << 4,
 };
 
 /*
diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c
index e1913d3..18424a2 100644
--- a/sound/usb/line6/pcm.c
+++ b/sound/usb/line6/pcm.c
@@ -52,7 +52,7 @@ static int snd_line6_impulse_volume_put(struct snd_kcontrol *kcontrol,
 
 	line6pcm->impulse_volume = value;
 	if (value > 0) {
-		err = line6_pcm_acquire(line6pcm, LINE6_STREAM_IMPULSE);
+		err = line6_pcm_acquire(line6pcm, LINE6_STREAM_IMPULSE, true);
 		if (err < 0) {
 			line6pcm->impulse_volume = 0;
 			return err;
@@ -242,6 +242,15 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
 		switch (cmd) {
 		case SNDRV_PCM_TRIGGER_START:
 		case SNDRV_PCM_TRIGGER_RESUME:
+			if ((s->stream == SNDRV_PCM_STREAM_CAPTURE) &&
+				(line6pcm->line6->properties->capabilities &
+					LINE6_CAP_IN_NEEDS_OUT)
+			) {
+				err = line6_stream_start(line6pcm, SNDRV_PCM_STREAM_PLAYBACK,
+						 LINE6_STREAM_CAPTURE_HELPER);
+				if (err < 0)
+					return err;
+			}
 			err = line6_stream_start(line6pcm, s->stream,
 						 LINE6_STREAM_PCM);
 			if (err < 0)
@@ -250,6 +259,13 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
 
 		case SNDRV_PCM_TRIGGER_STOP:
 		case SNDRV_PCM_TRIGGER_SUSPEND:
+			if ((s->stream == SNDRV_PCM_STREAM_CAPTURE) &&
+				(line6pcm->line6->properties->capabilities &
+					LINE6_CAP_IN_NEEDS_OUT)
+			) {
+				line6_stream_stop(line6pcm, SNDRV_PCM_STREAM_PLAYBACK,
+					  LINE6_STREAM_CAPTURE_HELPER);
+			}
 			line6_stream_stop(line6pcm, s->stream,
 					  LINE6_STREAM_PCM);
 			break;
@@ -283,27 +299,30 @@ snd_pcm_uframes_t snd_line6_pointer(struct snd_pcm_substream *substream)
 	return pstr->pos_done;
 }
 
-/* Acquire and start duplex streams:
+/* 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)
+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, type);
+		ret = line6_buffer_acquire(line6pcm, pstr, dir, 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;
+	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);
@@ -339,7 +358,7 @@ int snd_line6_hw_params(struct snd_pcm_substream *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);
+	ret = line6_buffer_acquire(line6pcm, pstr, substream->stream, LINE6_STREAM_PCM);
 	if (ret < 0)
 		goto error;
 
diff --git a/sound/usb/line6/pcm.h b/sound/usb/line6/pcm.h
index 58d36f9..5f796ef8 100644
--- a/sound/usb/line6/pcm.h
+++ b/sound/usb/line6/pcm.h
@@ -72,6 +72,7 @@ enum {
 	LINE6_STREAM_PCM,
 	LINE6_STREAM_MONITOR,
 	LINE6_STREAM_IMPULSE,
+	LINE6_STREAM_CAPTURE_HELPER,
 };
 
 /* misc bit flags for PCM operation */
@@ -190,7 +191,8 @@ extern int snd_line6_hw_params(struct snd_pcm_substream *substream,
 extern int snd_line6_hw_free(struct snd_pcm_substream *substream);
 extern snd_pcm_uframes_t snd_line6_pointer(struct snd_pcm_substream *substream);
 extern void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm);
-extern int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type);
+extern int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type,
+			       bool start);
 extern void line6_pcm_release(struct snd_line6_pcm *line6pcm, int type);
 
 #endif
diff --git a/sound/usb/line6/toneport.c b/sound/usb/line6/toneport.c
index da76e03..8e22f43 100644
--- a/sound/usb/line6/toneport.c
+++ b/sound/usb/line6/toneport.c
@@ -177,7 +177,7 @@ static int snd_toneport_monitor_put(struct snd_kcontrol *kcontrol,
 	line6pcm->volume_monitor = ucontrol->value.integer.value[0];
 
 	if (line6pcm->volume_monitor > 0) {
-		err = line6_pcm_acquire(line6pcm, LINE6_STREAM_MONITOR);
+		err = line6_pcm_acquire(line6pcm, LINE6_STREAM_MONITOR, true);
 		if (err < 0) {
 			line6pcm->volume_monitor = 0;
 			line6_pcm_release(line6pcm, LINE6_STREAM_MONITOR);
@@ -246,7 +246,7 @@ static void toneport_start_pcm(unsigned long arg)
 	struct usb_line6_toneport *toneport = (struct usb_line6_toneport *)arg;
 	struct usb_line6 *line6 = &toneport->line6;
 
-	line6_pcm_acquire(line6->line6pcm, LINE6_STREAM_MONITOR);
+	line6_pcm_acquire(line6->line6pcm, LINE6_STREAM_MONITOR, true);
 }
 
 /* control definition */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v2 3/9] ALSA: line6: Distinguish device init (ctrl EP) and MIDI data transfer (int EP)
  2016-08-18 22:20 ` [PATCH v2 0/9] " Andrej Krutak
  2016-08-18 22:20   ` [PATCH v2 1/9] ALSA: line6: Make driver configuration more generic Andrej Krutak
  2016-08-18 22:20   ` [PATCH v2 2/9] ALSA: line6: Add LINE6_CAP_IN_NEEDS_OUT, a void playback stream during capture Andrej Krutak
@ 2016-08-18 22:20   ` Andrej Krutak
  2016-08-24 14:56     ` Takashi Iwai
  2016-08-18 22:20   ` [PATCH v2 4/9] ALSA: line6: Add support for POD X3 Andrej Krutak
                     ` (5 subsequent siblings)
  8 siblings, 1 reply; 81+ messages in thread
From: Andrej Krutak @ 2016-08-18 22:20 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

POD X3 can initialize similarly to older PODs, but it doesn't have the MIDI
interface. Instead, configuration is done via proprietary bulk EP messages.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/driver.c | 10 +++++-----
 sound/usb/line6/driver.h |  6 ++++--
 sound/usb/line6/pod.c    |  9 ++++++++-
 sound/usb/line6/variax.c |  6 ++++--
 4 files changed, 21 insertions(+), 10 deletions(-)

diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index efeb16a8..5fd6cad 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -482,7 +482,7 @@ static void line6_get_interval(struct usb_line6 *line6)
 	}
 }
 
-static int line6_init_cap_control(struct usb_line6 *line6)
+static int line6_init_cap_control_midi(struct usb_line6 *line6)
 {
 	int ret;
 
@@ -572,8 +572,8 @@ int line6_probe(struct usb_interface *interface,
 
 	line6_get_interval(line6);
 
-	if (properties->capabilities & LINE6_CAP_CONTROL) {
-		ret = line6_init_cap_control(line6);
+	if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+		ret = line6_init_cap_control_midi(line6);
 		if (ret < 0)
 			goto error;
 	}
@@ -643,7 +643,7 @@ int line6_suspend(struct usb_interface *interface, pm_message_t message)
 
 	snd_power_change_state(line6->card, SNDRV_CTL_POWER_D3hot);
 
-	if (line6->properties->capabilities & LINE6_CAP_CONTROL)
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)
 		line6_stop_listen(line6);
 
 	if (line6pcm != NULL) {
@@ -662,7 +662,7 @@ int line6_resume(struct usb_interface *interface)
 {
 	struct usb_line6 *line6 = usb_get_intfdata(interface);
 
-	if (line6->properties->capabilities & LINE6_CAP_CONTROL)
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)
 		line6_start_listen(line6);
 
 	snd_power_change_state(line6->card, SNDRV_CTL_POWER_D0);
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index 69658dc..7aeb6ad 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -102,6 +102,8 @@ enum {
 	LINE6_CAP_PCM =		1 << 1,
 	/* device supports hardware monitoring */
 	LINE6_CAP_HWMON =	1 << 2,
+	/* device uses raw MIDI via USB (data endpoints) */
+	LINE6_CAP_CONTROL_MIDI = 1 << 3,
 	/* device requires output data when input is read */
 	LINE6_CAP_IN_NEEDS_OUT = 1 << 4,
 };
@@ -142,10 +144,10 @@ struct usb_line6 {
 	/* Line 6 MIDI device data structure */
 	struct snd_line6_midi *line6midi;
 
-	/* URB for listening to PODxt Pro control endpoint */
+	/* URB for listening to POD data endpoint */
 	struct urb *urb_listen;
 
-	/* Buffer for listening to PODxt Pro control endpoint */
+	/* Buffer for listening to POD data endpoint */
 	unsigned char *buffer_listen;
 
 	/* Buffer for message to be processed */
diff --git a/sound/usb/line6/pod.c b/sound/usb/line6/pod.c
index 36e7274..17aa616 100644
--- a/sound/usb/line6/pod.c
+++ b/sound/usb/line6/pod.c
@@ -475,6 +475,7 @@ static const struct line6_properties pod_properties_table[] = {
 		.id = "BassPODxt",
 		.name = "BassPODxt",
 		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI
 				| LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 5,
@@ -487,6 +488,7 @@ static const struct line6_properties pod_properties_table[] = {
 		.id = "BassPODxtLive",
 		.name = "BassPODxt Live",
 		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI
 				| LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 1,
@@ -499,6 +501,7 @@ static const struct line6_properties pod_properties_table[] = {
 		.id = "BassPODxtPro",
 		.name = "BassPODxt Pro",
 		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI
 				| LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 5,
@@ -510,7 +513,8 @@ static const struct line6_properties pod_properties_table[] = {
 	[LINE6_POCKETPOD] = {
 		.id = "PocketPOD",
 		.name = "Pocket POD",
-		.capabilities	= LINE6_CAP_CONTROL,
+		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI,
 		.altsetting = 0,
 		.ep_ctrl_r = 0x82,
 		.ep_ctrl_w = 0x02,
@@ -520,6 +524,7 @@ static const struct line6_properties pod_properties_table[] = {
 		.id = "PODxt",
 		.name = "PODxt",
 		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI
 				| LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 5,
@@ -532,6 +537,7 @@ static const struct line6_properties pod_properties_table[] = {
 		.id = "PODxtLive",
 		.name = "PODxt Live",
 		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI
 				| LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 1,
@@ -544,6 +550,7 @@ static const struct line6_properties pod_properties_table[] = {
 		.id = "PODxtPro",
 		.name = "PODxt Pro",
 		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI
 				| LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 5,
diff --git a/sound/usb/line6/variax.c b/sound/usb/line6/variax.c
index ddc23dd..0c4512d 100644
--- a/sound/usb/line6/variax.c
+++ b/sound/usb/line6/variax.c
@@ -259,7 +259,8 @@ static const struct line6_properties variax_properties_table[] = {
 	[LINE6_PODXTLIVE_VARIAX] = {
 		.id = "PODxtLive",
 		.name = "PODxt Live",
-		.capabilities	= LINE6_CAP_CONTROL,
+		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI,
 		.altsetting = 1,
 		.ep_ctrl_r = 0x86,
 		.ep_ctrl_w = 0x05,
@@ -269,7 +270,8 @@ static const struct line6_properties variax_properties_table[] = {
 	[LINE6_VARIAX] = {
 		.id = "Variax",
 		.name = "Variax Workbench",
-		.capabilities	= LINE6_CAP_CONTROL,
+		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI,
 		.altsetting = 1,
 		.ep_ctrl_r = 0x82,
 		.ep_ctrl_w = 0x01,
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v2 4/9] ALSA: line6: Add support for POD X3
  2016-08-18 22:20 ` [PATCH v2 0/9] " Andrej Krutak
                     ` (2 preceding siblings ...)
  2016-08-18 22:20   ` [PATCH v2 3/9] ALSA: line6: Distinguish device init (ctrl EP) and MIDI data transfer (int EP) Andrej Krutak
@ 2016-08-18 22:20   ` Andrej Krutak
  2016-08-18 22:20   ` [PATCH v2 5/9] ALSA: line6: Add support for POD X3 Live (only USB ID differs from POD X3) Andrej Krutak
                     ` (4 subsequent siblings)
  8 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-08-18 22:20 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

This includes audio in/out and basic initialization via control EP (emulates
what original driver does). The initialization is done similarly to original
POD, firmware and serial IDs are read and exported via sysfs.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/Kconfig |   4 +-
 sound/usb/line6/podhd.c | 276 +++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 265 insertions(+), 15 deletions(-)

diff --git a/sound/usb/line6/Kconfig b/sound/usb/line6/Kconfig
index f4585d37..8ffcf48 100644
--- a/sound/usb/line6/Kconfig
+++ b/sound/usb/line6/Kconfig
@@ -21,10 +21,10 @@ config SND_USB_POD
 	      re-amping)
 
 config SND_USB_PODHD
-	tristate "Line 6 POD HD300/400/500 USB support"
+	tristate "Line 6 POD X3/HD300/400/500 USB support"
 	select SND_USB_LINE6
 	help
-	  This is a driver for POD HD300, 400 and 500 devices.
+	  This is a driver for POD X3, HD300, 400 and 500 devices.
 
 config SND_USB_TONEPORT
 	tristate "TonePort GX, UX1 and UX2 USB support"
diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c
index 4fc4789..8cf0a98 100644
--- a/sound/usb/line6/podhd.c
+++ b/sound/usb/line6/podhd.c
@@ -2,6 +2,7 @@
  * Line 6 Pod HD
  *
  * Copyright (C) 2011 Stefan Hajnoczi <stefanha@gmail.com>
+ * Copyright (C) 2015 Andrej Krutak <dev@andree.sk>
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License as
@@ -18,11 +19,44 @@
 #include "driver.h"
 #include "pcm.h"
 
+#define PODHD_STARTUP_DELAY 500
+
+/*
+ * Stages of POD startup procedure
+ */
+enum {
+	PODHD_STARTUP_INIT = 1,
+	PODHD_STARTUP_SCHEDULE_WORKQUEUE,
+	PODHD_STARTUP_SETUP,
+	PODHD_STARTUP_LAST = PODHD_STARTUP_SETUP - 1
+};
+
 enum {
 	LINE6_PODHD300,
 	LINE6_PODHD400,
 	LINE6_PODHD500_0,
 	LINE6_PODHD500_1,
+	LINE6_PODX3,
+};
+
+struct usb_line6_podhd {
+	/* Generic Line 6 USB data */
+	struct usb_line6 line6;
+
+	/* Timer for device initialization */
+	struct timer_list startup_timer;
+
+	/* Work handler for device initialization */
+	struct work_struct startup_work;
+
+	/* Current progress in startup procedure */
+	int startup_progress;
+
+	/* Serial number of device */
+	u32 serial_number;
+
+	/* Firmware version */
+	int firmware_version;
 };
 
 static struct snd_ratden podhd_ratden = {
@@ -71,9 +105,196 @@ static struct line6_pcm_properties podhd_pcm_properties = {
 	.rates = {
 			    .nrats = 1,
 			    .rats = &podhd_ratden},
-	.bytes_per_channel = 3 /* 24bit audio (stereo) */
+	.bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */
 };
 
+static struct line6_pcm_properties podx3_pcm_properties = {
+	.playback_hw = {
+				  .info = (SNDRV_PCM_INFO_MMAP |
+					   SNDRV_PCM_INFO_INTERLEAVED |
+					   SNDRV_PCM_INFO_BLOCK_TRANSFER |
+					   SNDRV_PCM_INFO_MMAP_VALID |
+					   SNDRV_PCM_INFO_PAUSE |
+					   SNDRV_PCM_INFO_SYNC_START),
+				  .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+				  .rates = SNDRV_PCM_RATE_48000,
+				  .rate_min = 48000,
+				  .rate_max = 48000,
+				  .channels_min = 2,
+				  .channels_max = 2,
+				  .buffer_bytes_max = 60000,
+				  .period_bytes_min = 64,
+				  .period_bytes_max = 8192,
+				  .periods_min = 1,
+				  .periods_max = 1024},
+	.capture_hw = {
+				 .info = (SNDRV_PCM_INFO_MMAP |
+					  SNDRV_PCM_INFO_INTERLEAVED |
+					  SNDRV_PCM_INFO_BLOCK_TRANSFER |
+					  SNDRV_PCM_INFO_MMAP_VALID |
+					  SNDRV_PCM_INFO_SYNC_START),
+				 .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+				 .rates = SNDRV_PCM_RATE_48000,
+				 .rate_min = 48000,
+				 .rate_max = 48000,
+				 /* 1+2: Main signal (out), 3+4: Tone 1,
+				  * 5+6: Tone 2, 7+8: raw */
+				 .channels_min = 8,
+				 .channels_max = 8,
+				 .buffer_bytes_max = 60000,
+				 .period_bytes_min = 64,
+				 .period_bytes_max = 8192,
+				 .periods_min = 1,
+				 .periods_max = 1024},
+	.rates = {
+			    .nrats = 1,
+			    .rats = &podhd_ratden},
+	.bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */
+};
+
+static void podhd_startup_start_workqueue(unsigned long data);
+static void podhd_startup_workqueue(struct work_struct *work);
+static int podhd_startup_finalize(struct usb_line6_podhd *pod);
+
+static ssize_t serial_number_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct snd_card *card = dev_to_snd_card(dev);
+	struct usb_line6_podhd *pod = card->private_data;
+
+	return sprintf(buf, "%u\n", pod->serial_number);
+}
+
+static ssize_t firmware_version_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct snd_card *card = dev_to_snd_card(dev);
+	struct usb_line6_podhd *pod = card->private_data;
+
+	return sprintf(buf, "%06x\n", pod->firmware_version);
+}
+
+static DEVICE_ATTR_RO(firmware_version);
+static DEVICE_ATTR_RO(serial_number);
+
+static struct attribute *podhd_dev_attrs[] = {
+	&dev_attr_firmware_version.attr,
+	&dev_attr_serial_number.attr,
+	NULL
+};
+
+static const struct attribute_group podhd_dev_attr_group = {
+	.name = "podhd",
+	.attrs = podhd_dev_attrs,
+};
+
+/*
+ * POD X3 startup procedure.
+ *
+ * May be compatible with other POD HD's, since it's also similar to the
+ * previous POD setup. In any case, it doesn't seem to be required for the
+ * audio nor bulk interfaces to work.
+ */
+
+static void podhd_startup(struct usb_line6_podhd *pod)
+{
+	CHECK_STARTUP_PROGRESS(pod->startup_progress, PODHD_STARTUP_INIT);
+
+	/* delay startup procedure: */
+	line6_start_timer(&pod->startup_timer, PODHD_STARTUP_DELAY,
+		podhd_startup_start_workqueue, (unsigned long)pod);
+}
+
+static void podhd_startup_start_workqueue(unsigned long data)
+{
+	struct usb_line6_podhd *pod = (struct usb_line6_podhd *)data;
+
+	CHECK_STARTUP_PROGRESS(pod->startup_progress,
+		PODHD_STARTUP_SCHEDULE_WORKQUEUE);
+
+	/* schedule work for global work queue: */
+	schedule_work(&pod->startup_work);
+}
+
+static int podhd_dev_start(struct usb_line6_podhd *pod)
+{
+	int ret;
+	u8 init_bytes[8];
+	int i;
+	struct usb_device *usbdev = pod->line6.usbdev;
+
+	ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
+					0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+					0x11, 0,
+					NULL, 0, LINE6_TIMEOUT * HZ);
+	if (ret < 0) {
+		dev_err(pod->line6.ifcdev, "read request failed (error %d)\n", ret);
+		return ret;
+	}
+
+	/* NOTE: looks like some kind of ping message */
+	ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67,
+					USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+					0x11, 0x0,
+					&init_bytes, 3, LINE6_TIMEOUT * HZ);
+	if (ret < 0) {
+		dev_err(pod->line6.ifcdev,
+			"receive length failed (error %d)\n", ret);
+		return ret;
+	}
+
+	pod->firmware_version =
+		(init_bytes[0] << 16) | (init_bytes[1] << 8) | (init_bytes[2] << 0);
+
+	for (i = 0; i <= 16; i++) {
+		ret = line6_read_data(&pod->line6, 0xf000 + 0x08 * i, init_bytes, 8);
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
+					USB_REQ_SET_FEATURE,
+					USB_TYPE_STANDARD | USB_RECIP_DEVICE | USB_DIR_OUT,
+					1, 0,
+					NULL, 0, LINE6_TIMEOUT * HZ);
+	if (ret < 0) {
+		return ret;
+	}
+
+	return 0;
+}
+
+static void podhd_startup_workqueue(struct work_struct *work)
+{
+	struct usb_line6_podhd *pod =
+	    container_of(work, struct usb_line6_podhd, startup_work);
+
+	CHECK_STARTUP_PROGRESS(pod->startup_progress, PODHD_STARTUP_SETUP);
+
+	podhd_dev_start(pod);
+	line6_read_serial_number(&pod->line6, &pod->serial_number);
+
+	podhd_startup_finalize(pod);
+}
+
+static int podhd_startup_finalize(struct usb_line6_podhd *pod)
+{
+	struct usb_line6 *line6 = &pod->line6;
+
+	/* ALSA audio interface: */
+	return snd_card_register(line6->card);
+}
+
+static void podhd_disconnect(struct usb_line6 *line6)
+{
+	struct usb_line6_podhd *pod = (struct usb_line6_podhd *)line6;
+
+	if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) {
+		del_timer_sync(&pod->startup_timer);
+		cancel_work_sync(&pod->startup_work);
+	}
+}
+
 /*
 	Try to init POD HD device.
 */
@@ -81,6 +302,16 @@ static int podhd_init(struct usb_line6 *line6,
 		      const struct usb_device_id *id)
 {
 	int err;
+	struct usb_line6_podhd *pod = (struct usb_line6_podhd *) line6;
+
+	line6->disconnect = podhd_disconnect;
+
+	if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) {
+		/* create sysfs entries: */
+		err = snd_card_add_dev_attr(line6->card, &podhd_dev_attr_group);
+		if (err < 0)
+			return err;
+	}
 
 	/* initialize MIDI subsystem: */
 	err = line6_init_midi(line6);
@@ -88,12 +319,22 @@ static int podhd_init(struct usb_line6 *line6,
 		return err;
 
 	/* initialize PCM subsystem: */
-	err = line6_init_pcm(line6, &podhd_pcm_properties);
+	err = line6_init_pcm(line6,
+		(id->driver_info == LINE6_PODX3) ? &podx3_pcm_properties :
+		&podhd_pcm_properties);
 	if (err < 0)
 		return err;
 
-	/* register USB audio system: */
-	return snd_card_register(line6->card);
+	if (!(pod->line6.properties->capabilities & LINE6_CAP_CONTROL)) {
+		/* register USB audio system directly */
+		return podhd_startup_finalize(pod);
+	}
+
+	/* init device and delay registering */
+	init_timer(&pod->startup_timer);
+	INIT_WORK(&pod->startup_work, podhd_startup_workqueue);
+	podhd_startup(pod);
+	return 0;
 }
 
 #define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod)
@@ -101,10 +342,12 @@ static int podhd_init(struct usb_line6 *line6,
 
 /* table of devices that work with this driver */
 static const struct usb_device_id podhd_id_table[] = {
+	/* TODO: no need to alloc data interfaces when only audio is used */
 	{ LINE6_DEVICE(0x5057),    .driver_info = LINE6_PODHD300 },
 	{ LINE6_DEVICE(0x5058),    .driver_info = LINE6_PODHD400 },
 	{ LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500_0 },
 	{ LINE6_IF_NUM(0x414D, 1), .driver_info = LINE6_PODHD500_1 },
+	{ LINE6_IF_NUM(0x414A, 0), .driver_info = LINE6_PODX3 },
 	{}
 };
 
@@ -114,8 +357,7 @@ static const struct line6_properties podhd_properties_table[] = {
 	[LINE6_PODHD300] = {
 		.id = "PODHD300",
 		.name = "POD HD300",
-		.capabilities	= LINE6_CAP_CONTROL
-				| LINE6_CAP_PCM
+		.capabilities	= LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 5,
 		.ep_ctrl_r = 0x84,
@@ -126,8 +368,7 @@ static const struct line6_properties podhd_properties_table[] = {
 	[LINE6_PODHD400] = {
 		.id = "PODHD400",
 		.name = "POD HD400",
-		.capabilities	= LINE6_CAP_CONTROL
-				| LINE6_CAP_PCM
+		.capabilities	= LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 5,
 		.ep_ctrl_r = 0x84,
@@ -138,8 +379,7 @@ static const struct line6_properties podhd_properties_table[] = {
 	[LINE6_PODHD500_0] = {
 		.id = "PODHD500",
 		.name = "POD HD500",
-		.capabilities	= LINE6_CAP_CONTROL
-				| LINE6_CAP_PCM
+		.capabilities	= LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 1,
 		.ep_ctrl_r = 0x81,
@@ -150,8 +390,7 @@ static const struct line6_properties podhd_properties_table[] = {
 	[LINE6_PODHD500_1] = {
 		.id = "PODHD500",
 		.name = "POD HD500",
-		.capabilities	= LINE6_CAP_CONTROL
-				| LINE6_CAP_PCM
+		.capabilities	= LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 1,
 		.ep_ctrl_r = 0x81,
@@ -159,6 +398,17 @@ static const struct line6_properties podhd_properties_table[] = {
 		.ep_audio_r = 0x86,
 		.ep_audio_w = 0x02,
 	},
+	[LINE6_PODX3] = {
+		.id = "PODX3",
+		.name = "POD X3",
+		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_PCM | LINE6_CAP_HWMON | LINE6_CAP_IN_NEEDS_OUT,
+		.altsetting = 1,
+		.ep_ctrl_r = 0x81,
+		.ep_ctrl_w = 0x01,
+		.ep_audio_r = 0x86,
+		.ep_audio_w = 0x02,
+	},
 };
 
 /*
@@ -169,7 +419,7 @@ static int podhd_probe(struct usb_interface *interface,
 {
 	return line6_probe(interface, id, "Line6-PODHD",
 			   &podhd_properties_table[id->driver_info],
-			   podhd_init, sizeof(struct usb_line6));
+			   podhd_init, sizeof(struct usb_line6_podhd));
 }
 
 static struct usb_driver podhd_driver = {
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v2 5/9] ALSA: line6: Add support for POD X3 Live (only USB ID differs from POD X3)
  2016-08-18 22:20 ` [PATCH v2 0/9] " Andrej Krutak
                     ` (3 preceding siblings ...)
  2016-08-18 22:20   ` [PATCH v2 4/9] ALSA: line6: Add support for POD X3 Andrej Krutak
@ 2016-08-18 22:20   ` Andrej Krutak
  2016-08-18 22:20   ` [PATCH v2 6/9] ALSA: line6: Allow bulk endpoints instead of interrupt endpoints Andrej Krutak
                     ` (3 subsequent siblings)
  8 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-08-18 22:20 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/podhd.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c
index 8cf0a98..8246ea5 100644
--- a/sound/usb/line6/podhd.c
+++ b/sound/usb/line6/podhd.c
@@ -37,6 +37,7 @@ enum {
 	LINE6_PODHD500_0,
 	LINE6_PODHD500_1,
 	LINE6_PODX3,
+	LINE6_PODX3LIVE
 };
 
 struct usb_line6_podhd {
@@ -348,6 +349,7 @@ static const struct usb_device_id podhd_id_table[] = {
 	{ LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500_0 },
 	{ LINE6_IF_NUM(0x414D, 1), .driver_info = LINE6_PODHD500_1 },
 	{ LINE6_IF_NUM(0x414A, 0), .driver_info = LINE6_PODX3 },
+	{ LINE6_IF_NUM(0x414B, 0), .driver_info = LINE6_PODX3LIVE },
 	{}
 };
 
@@ -409,6 +411,17 @@ static const struct line6_properties podhd_properties_table[] = {
 		.ep_audio_r = 0x86,
 		.ep_audio_w = 0x02,
 	},
+	[LINE6_PODX3LIVE] = {
+		.id = "PODX3LIVE",
+		.name = "POD X3 LIVE",
+		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_PCM | LINE6_CAP_HWMON | LINE6_CAP_IN_NEEDS_OUT,
+		.altsetting = 1,
+		.ep_ctrl_r = 0x81,
+		.ep_ctrl_w = 0x01,
+		.ep_audio_r = 0x86,
+		.ep_audio_w = 0x02,
+	},
 };
 
 /*
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v2 6/9] ALSA: line6: Allow bulk endpoints instead of interrupt endpoints
  2016-08-18 22:20 ` [PATCH v2 0/9] " Andrej Krutak
                     ` (4 preceding siblings ...)
  2016-08-18 22:20   ` [PATCH v2 5/9] ALSA: line6: Add support for POD X3 Live (only USB ID differs from POD X3) Andrej Krutak
@ 2016-08-18 22:20   ` Andrej Krutak
  2016-08-24 15:02     ` Takashi Iwai
  2016-08-18 22:20   ` [PATCH v2 7/9] ALSA: line6: Allow processing of raw incoming messages Andrej Krutak
                     ` (2 subsequent siblings)
  8 siblings, 1 reply; 81+ messages in thread
From: Andrej Krutak @ 2016-08-18 22:20 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

Some PODs (e.g. POD X3) have bulk instead of interrupt endpoints for
data transfer.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/driver.c | 63 ++++++++++++++++++++++++++++++++++++------------
 1 file changed, 48 insertions(+), 15 deletions(-)

diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 5fd6cad..9b16777 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -66,10 +66,17 @@ static int line6_start_listen(struct usb_line6 *line6)
 {
 	int err;
 
-	usb_fill_int_urb(line6->urb_listen, line6->usbdev,
-		usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r),
-		line6->buffer_listen, LINE6_BUFSIZE_LISTEN,
-		line6_data_received, line6, line6->interval);
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+		usb_fill_int_urb(line6->urb_listen, line6->usbdev,
+			usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r),
+			line6->buffer_listen, LINE6_BUFSIZE_LISTEN,
+			line6_data_received, line6, line6->interval);
+	} else {
+		usb_fill_bulk_urb(line6->urb_listen, line6->usbdev,
+			usb_rcvbulkpipe(line6->usbdev, line6->properties->ep_ctrl_r),
+			line6->buffer_listen, LINE6_BUFSIZE_LISTEN,
+			line6_data_received, line6);
+	}
 	line6->urb_listen->actual_length = 0;
 	err = usb_submit_urb(line6->urb_listen, GFP_ATOMIC);
 	return err;
@@ -90,6 +97,7 @@ static int line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
 				  int size)
 {
 	int i, done = 0;
+	const struct line6_properties *properties = line6->properties;
 
 	for (i = 0; i < size; i += line6->max_packet_size) {
 		int partial;
@@ -97,15 +105,21 @@ static int line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
 		int frag_size = min(line6->max_packet_size, size - i);
 		int retval;
 
-		retval = usb_interrupt_msg(line6->usbdev,
-					usb_sndintpipe(line6->usbdev,
-						line6->properties->ep_ctrl_w),
-					(char *)frag_buf, frag_size,
-					&partial, LINE6_TIMEOUT * HZ);
+		if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+			retval = usb_interrupt_msg(line6->usbdev,
+						usb_sndintpipe(line6->usbdev, properties->ep_ctrl_w),
+						(char *)frag_buf, frag_size,
+						&partial, LINE6_TIMEOUT * HZ);
+		} else {
+			retval = usb_bulk_msg(line6->usbdev,
+						usb_sndbulkpipe(line6->usbdev, properties->ep_ctrl_w),
+						(char *)frag_buf, frag_size,
+						&partial, LINE6_TIMEOUT * HZ);
+		}
 
 		if (retval) {
 			dev_err(line6->ifcdev,
-				"usb_interrupt_msg failed (%d)\n", retval);
+				"usb_bulk_msg failed (%d)\n", retval);
 			break;
 		}
 
@@ -140,10 +154,17 @@ static int line6_send_raw_message_async_part(struct message *msg,
 	int done = msg->done;
 	int bytes = min(msg->size - done, line6->max_packet_size);
 
-	usb_fill_int_urb(urb, line6->usbdev,
-		usb_sndintpipe(line6->usbdev, line6->properties->ep_ctrl_w),
-		(char *)msg->buffer + done, bytes,
-		line6_async_request_sent, msg, line6->interval);
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+		usb_fill_int_urb(urb, line6->usbdev,
+			usb_sndintpipe(line6->usbdev, line6->properties->ep_ctrl_w),
+			(char *)msg->buffer + done, bytes,
+			line6_async_request_sent, msg, line6->interval);
+	} else {
+		usb_fill_bulk_urb(urb, line6->usbdev,
+			usb_sndbulkpipe(line6->usbdev, line6->properties->ep_ctrl_w),
+			(char *)msg->buffer + done, bytes,
+			line6_async_request_sent, msg);
+	}
 
 	msg->done += bytes;
 	retval = usb_submit_urb(urb, GFP_ATOMIC);
@@ -462,7 +483,19 @@ static void line6_destruct(struct snd_card *card)
 static void line6_get_interval(struct usb_line6 *line6)
 {
 	struct usb_device *usbdev = line6->usbdev;
-	struct usb_host_endpoint *ep = usbdev->ep_in[line6->properties->ep_ctrl_r];
+	const struct line6_properties *properties = line6->properties;
+	int pipe;
+	struct usb_host_endpoint *ep;
+
+	if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+		pipe =
+			usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r);
+	} else {
+		pipe =
+			usb_rcvbulkpipe(line6->usbdev, line6->properties->ep_ctrl_r);
+	}
+	ep = usbdev->ep_in[usb_pipeendpoint(pipe)];
+
 	if (ep) {
 		line6->interval = ep->desc.bInterval;
 		if (usbdev->speed == USB_SPEED_LOW) {
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v2 7/9] ALSA: line6: Allow processing of raw incoming messages
  2016-08-18 22:20 ` [PATCH v2 0/9] " Andrej Krutak
                     ` (5 preceding siblings ...)
  2016-08-18 22:20   ` [PATCH v2 6/9] ALSA: line6: Allow bulk endpoints instead of interrupt endpoints Andrej Krutak
@ 2016-08-18 22:20   ` Andrej Krutak
  2016-08-24 15:05     ` Takashi Iwai
  2016-08-18 22:20   ` [PATCH v2 8/9] ALSA: line6: Cleanup initialization Andrej Krutak
  2016-08-18 22:20   ` [PATCH v2 9/9] ALSA: line6: Add hwdep interface to access the POD control messages Andrej Krutak
  8 siblings, 1 reply; 81+ messages in thread
From: Andrej Krutak @ 2016-08-18 22:20 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

Not all PODs use MIDI via USB data interface, thus allow avoiding
that code and instead using direct processing.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/driver.c | 59 ++++++++++++++++++++++++++++--------------------
 sound/usb/line6/driver.h |  8 ++++---
 sound/usb/line6/midi.c   |  2 +-
 3 files changed, 40 insertions(+), 29 deletions(-)

diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 9b16777..853a143 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -290,26 +290,31 @@ static void line6_data_received(struct urb *urb)
 	if (urb->status == -ESHUTDOWN)
 		return;
 
-	done =
-	    line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length);
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+		done =
+			line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length);
 
-	if (done < urb->actual_length) {
-		line6_midibuf_ignore(mb, done);
-		dev_dbg(line6->ifcdev, "%d %d buffer overflow - message skipped\n",
-			done, urb->actual_length);
-	}
+		if (done < urb->actual_length) {
+			line6_midibuf_ignore(mb, done);
+			dev_dbg(line6->ifcdev, "%d %d buffer overflow - message skipped\n",
+				done, urb->actual_length);
+		}
 
-	for (;;) {
-		done =
-		    line6_midibuf_read(mb, line6->buffer_message,
-				       LINE6_MESSAGE_MAXLEN);
+		for (;;) {
+			done =
+				line6_midibuf_read(mb, line6->buffer_message,
+						LINE6_MESSAGE_MAXLEN);
 
-		if (done == 0)
-			break;
+			if (done == 0)
+				break;
 
-		line6->message_length = done;
-		line6_midi_receive(line6, line6->buffer_message, done);
+			line6->message_length = done;
+			line6_midi_receive(line6, line6->buffer_message, done);
 
+			if (line6->process_message)
+				line6->process_message(line6);
+		}
+	} else {
 		if (line6->process_message)
 			line6->process_message(line6);
 	}
@@ -469,7 +474,9 @@ static void line6_destruct(struct snd_card *card)
 	struct usb_device *usbdev = line6->usbdev;
 
 	/* free buffer memory first: */
-	kfree(line6->buffer_message);
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)
+		kfree(line6->buffer_message);
+
 	kfree(line6->buffer_listen);
 
 	/* then free URBs: */
@@ -515,7 +522,7 @@ static void line6_get_interval(struct usb_line6 *line6)
 	}
 }
 
-static int line6_init_cap_control_midi(struct usb_line6 *line6)
+static int line6_init_cap_control(struct usb_line6 *line6)
 {
 	int ret;
 
@@ -524,14 +531,16 @@ static int line6_init_cap_control_midi(struct usb_line6 *line6)
 	if (!line6->buffer_listen)
 		return -ENOMEM;
 
-	line6->buffer_message = kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL);
-	if (!line6->buffer_message)
-		return -ENOMEM;
-
 	line6->urb_listen = usb_alloc_urb(0, GFP_KERNEL);
 	if (!line6->urb_listen)
 		return -ENOMEM;
 
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+		line6->buffer_message = kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL);
+		if (!line6->buffer_message)
+			return -ENOMEM;
+	}
+
 	ret = line6_start_listen(line6);
 	if (ret < 0) {
 		dev_err(line6->ifcdev, "cannot start listening: %d\n", ret);
@@ -605,8 +614,8 @@ int line6_probe(struct usb_interface *interface,
 
 	line6_get_interval(line6);
 
-	if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
-		ret = line6_init_cap_control_midi(line6);
+	if (properties->capabilities & LINE6_CAP_CONTROL) {
+		ret = line6_init_cap_control(line6);
 		if (ret < 0)
 			goto error;
 	}
@@ -676,7 +685,7 @@ int line6_suspend(struct usb_interface *interface, pm_message_t message)
 
 	snd_power_change_state(line6->card, SNDRV_CTL_POWER_D3hot);
 
-	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL)
 		line6_stop_listen(line6);
 
 	if (line6pcm != NULL) {
@@ -695,7 +704,7 @@ int line6_resume(struct usb_interface *interface)
 {
 	struct usb_line6 *line6 = usb_get_intfdata(interface);
 
-	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL)
 		line6_start_listen(line6);
 
 	snd_power_change_state(line6->card, SNDRV_CTL_POWER_D0);
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index 7aeb6ad..14093c5 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -147,15 +147,17 @@ struct usb_line6 {
 	/* URB for listening to POD data endpoint */
 	struct urb *urb_listen;
 
-	/* Buffer for listening to POD data endpoint */
+	/* Buffer for incoming data from POD data endpoint */
 	unsigned char *buffer_listen;
 
-	/* Buffer for message to be processed */
+	/* Buffer for message to be processed, generated from MIDI layer */
 	unsigned char *buffer_message;
 
-	/* Length of message to be processed */
+	/* Length of message to be processed, generated from MIDI layer  */
 	int message_length;
 
+	/* If MIDI is supported, buffer_message contains the pre-processed data;
+	 * otherwise the data is only in urb_listen (buffer_incoming). */
 	void (*process_message)(struct usb_line6 *);
 	void (*disconnect)(struct usb_line6 *line6);
 };
diff --git a/sound/usb/line6/midi.c b/sound/usb/line6/midi.c
index cebea9b..d0fb2f2 100644
--- a/sound/usb/line6/midi.c
+++ b/sound/usb/line6/midi.c
@@ -258,7 +258,7 @@ int line6_init_midi(struct usb_line6 *line6)
 	struct snd_rawmidi *rmidi;
 	struct snd_line6_midi *line6midi;
 
-	if (!(line6->properties->capabilities & LINE6_CAP_CONTROL)) {
+	if (!(line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)) {
 		/* skip MIDI initialization and report success */
 		return 0;
 	}
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v2 8/9] ALSA: line6: Cleanup initialization
  2016-08-18 22:20 ` [PATCH v2 0/9] " Andrej Krutak
                     ` (6 preceding siblings ...)
  2016-08-18 22:20   ` [PATCH v2 7/9] ALSA: line6: Allow processing of raw incoming messages Andrej Krutak
@ 2016-08-18 22:20   ` Andrej Krutak
  2016-08-24 15:06     ` Takashi Iwai
  2016-08-18 22:20   ` [PATCH v2 9/9] ALSA: line6: Add hwdep interface to access the POD control messages Andrej Krutak
  8 siblings, 1 reply; 81+ messages in thread
From: Andrej Krutak @ 2016-08-18 22:20 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

Only determine control port properties if the devices needs it.
Only initialize PCM for POD HD devices that support it.
No POD HD seems to support MIDI, thus drop the initialization.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/driver.c |  3 +--
 sound/usb/line6/podhd.c  | 19 ++++++++-----------
 2 files changed, 9 insertions(+), 13 deletions(-)

diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 853a143..8a71d45 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -612,9 +612,8 @@ int line6_probe(struct usb_interface *interface,
 		goto error;
 	}
 
-	line6_get_interval(line6);
-
 	if (properties->capabilities & LINE6_CAP_CONTROL) {
+		line6_get_interval(line6);
 		ret = line6_init_cap_control(line6);
 		if (ret < 0)
 			goto error;
diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c
index 8246ea5..193eb29 100644
--- a/sound/usb/line6/podhd.c
+++ b/sound/usb/line6/podhd.c
@@ -314,17 +314,14 @@ static int podhd_init(struct usb_line6 *line6,
 			return err;
 	}
 
-	/* initialize MIDI subsystem: */
-	err = line6_init_midi(line6);
-	if (err < 0)
-		return err;
-
-	/* initialize PCM subsystem: */
-	err = line6_init_pcm(line6,
-		(id->driver_info == LINE6_PODX3) ? &podx3_pcm_properties :
-		&podhd_pcm_properties);
-	if (err < 0)
-		return err;
+	if (pod->line6.properties->capabilities & LINE6_CAP_PCM) {
+		/* initialize PCM subsystem: */
+		err = line6_init_pcm(line6,
+			(id->driver_info == LINE6_PODX3) ? &podx3_pcm_properties :
+			&podhd_pcm_properties);
+		if (err < 0)
+			return err;
+	}
 
 	if (!(pod->line6.properties->capabilities & LINE6_CAP_CONTROL)) {
 		/* register USB audio system directly */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v2 9/9] ALSA: line6: Add hwdep interface to access the POD control messages
  2016-08-18 22:20 ` [PATCH v2 0/9] " Andrej Krutak
                     ` (7 preceding siblings ...)
  2016-08-18 22:20   ` [PATCH v2 8/9] ALSA: line6: Cleanup initialization Andrej Krutak
@ 2016-08-18 22:20   ` Andrej Krutak
  8 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-08-18 22:20 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

We must do it this way, because e.g. POD X3 won't play any sound unless
the host listens on the bulk EP, so we cannot export it only via libusb.

The driver currently doesn't use the bulk EP messages in other way,
in future it could e.g. sense/modify volume(s).

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 include/uapi/sound/asound.h |   3 +-
 sound/usb/line6/driver.c    | 151 ++++++++++++++++++++++++++++++++++++++++++--
 sound/usb/line6/driver.h    |  23 ++++++-
 3 files changed, 170 insertions(+), 7 deletions(-)

diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index 609cadb..be353a7 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -106,9 +106,10 @@ enum {
 	SNDRV_HWDEP_IFACE_FW_OXFW,	/* Oxford OXFW970/971 based device */
 	SNDRV_HWDEP_IFACE_FW_DIGI00X,	/* Digidesign Digi 002/003 family */
 	SNDRV_HWDEP_IFACE_FW_TASCAM,	/* TASCAM FireWire series */
+	SNDRV_HWDEP_IFACE_LINE6,	/* Line6 USB processors */
 
 	/* Don't forget to change the following: */
-	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_TASCAM
+	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_LINE6
 };
 
 struct snd_hwdep_info {
diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 8a71d45..ac4c702 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -17,6 +17,7 @@
 
 #include <sound/core.h>
 #include <sound/initval.h>
+#include <sound/hwdep.h>
 
 #include "capture.h"
 #include "driver.h"
@@ -303,7 +304,7 @@ static void line6_data_received(struct urb *urb)
 		for (;;) {
 			done =
 				line6_midibuf_read(mb, line6->buffer_message,
-						LINE6_MESSAGE_MAXLEN);
+						LINE6_MIDI_MESSAGE_MAXLEN);
 
 			if (done == 0)
 				break;
@@ -315,8 +316,11 @@ static void line6_data_received(struct urb *urb)
 				line6->process_message(line6);
 		}
 	} else {
+		line6->buffer_message = urb->transfer_buffer;
+		line6->message_length = urb->actual_length;
 		if (line6->process_message)
 			line6->process_message(line6);
+		line6->buffer_message = NULL;
 	}
 
 	line6_start_listen(line6);
@@ -473,14 +477,16 @@ static void line6_destruct(struct snd_card *card)
 	struct usb_line6 *line6 = card->private_data;
 	struct usb_device *usbdev = line6->usbdev;
 
-	/* free buffer memory first: */
-	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)
+	/* Free buffer memory first. We cannot depend on the existence of private
+	 * data from the (podhd) module, it may be gone already during this call */
+	if (line6->buffer_message)
 		kfree(line6->buffer_message);
 
 	kfree(line6->buffer_listen);
 
 	/* then free URBs: */
 	usb_free_urb(line6->urb_listen);
+	line6->urb_listen = NULL;
 
 	/* decrement reference counters: */
 	usb_put_dev(usbdev);
@@ -522,6 +528,138 @@ static void line6_get_interval(struct usb_line6 *line6)
 	}
 }
 
+
+/* Enable buffering of incoming messages, flush the buffer */
+static int line6_hwdep_open(struct snd_hwdep *hw, struct file *file)
+{
+	struct usb_line6 *line6 = hw->private_data;
+
+	/* NOTE: hwdep layer provides atomicity here */
+
+	line6->messages.active = 1;
+
+	return 0;
+}
+
+/* Stop buffering */
+static int line6_hwdep_release(struct snd_hwdep *hw, struct file *file)
+{
+	struct usb_line6 *line6 = hw->private_data;
+
+	line6->messages.active = 0;
+
+	return 0;
+}
+
+/* Read from circular buffer, return to user */
+static long
+line6_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+					loff_t *offset)
+{
+	struct usb_line6 *line6 = hwdep->private_data;
+	long rv = 0;
+	unsigned int out_count;
+
+	if (mutex_lock_interruptible(&line6->messages.read_lock))
+		return -ERESTARTSYS;
+
+	while (kfifo_len(&line6->messages.fifo) == 0) {
+		mutex_unlock(&line6->messages.read_lock);
+
+		rv = wait_event_interruptible(
+			line6->messages.wait_queue,
+			kfifo_len(&line6->messages.fifo) != 0);
+		if (rv < 0)
+			return rv;
+
+		if (mutex_lock_interruptible(&line6->messages.read_lock))
+			return -ERESTARTSYS;
+	}
+
+	if (kfifo_peek_len(&line6->messages.fifo) > count) {
+		/* Buffer too small; allow re-read of the current item... */
+		rv = -EINVAL;
+	} else {
+		rv = kfifo_to_user(&line6->messages.fifo, buf, count, &out_count);
+		if (rv == 0)
+			rv = out_count;
+	}
+
+	mutex_unlock(&line6->messages.read_lock);
+	return rv;
+}
+
+/* Write directly (no buffering) to device by user*/
+static long
+line6_hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count,
+					loff_t *offset)
+{
+	struct usb_line6 *line6 = hwdep->private_data;
+	int rv;
+	char *data_copy;
+
+	if (count > line6->max_packet_size * LINE6_RAW_MESSAGES_MAXCOUNT) {
+		/* This is an arbitrary limit - still better than nothing... */
+		return -EINVAL;
+	}
+
+	data_copy = memdup_user(data, count);
+	if (IS_ERR(ERR_PTR))
+		return -ENOMEM;
+
+	rv = line6_send_raw_message(line6, data_copy, count);
+
+	kfree(data_copy);
+	return rv;
+}
+
+static const struct snd_hwdep_ops hwdep_ops = {
+	.open    = line6_hwdep_open,
+	.release = line6_hwdep_release,
+	.read    = line6_hwdep_read,
+	.write   = line6_hwdep_write,
+};
+
+/* Insert into circular buffer */
+static void line6_hwdep_push_message(struct usb_line6 *line6)
+{
+	if (!line6->messages.active)
+		return;
+
+	if (kfifo_avail(&line6->messages.fifo) >= line6->message_length) {
+		/* No race condition here, there's only one writer */
+		kfifo_in(&line6->messages.fifo,
+			line6->buffer_message, line6->message_length);
+	} /* else TODO: signal overflow */
+
+	wake_up_interruptible(&line6->messages.wait_queue);
+}
+
+static int line6_hwdep_init(struct usb_line6 *line6)
+{
+	int err;
+	struct snd_hwdep *hwdep;
+
+	/* TODO: usb_driver_claim_interface(); */
+	line6->process_message = line6_hwdep_push_message;
+	line6->messages.active = 0;
+	init_waitqueue_head(&line6->messages.wait_queue);
+	mutex_init(&line6->messages.read_lock);
+	INIT_KFIFO(line6->messages.fifo);
+
+	err = snd_hwdep_new(line6->card, "config", 0, &hwdep);
+	if (err < 0)
+		goto end;
+	strcpy(hwdep->name, "config");
+	hwdep->iface = SNDRV_HWDEP_IFACE_LINE6;
+	hwdep->ops = hwdep_ops;
+	hwdep->private_data = line6;
+	hwdep->exclusive = true;
+
+end:
+	return err;
+}
+
 static int line6_init_cap_control(struct usb_line6 *line6)
 {
 	int ret;
@@ -536,9 +674,13 @@ static int line6_init_cap_control(struct usb_line6 *line6)
 		return -ENOMEM;
 
 	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
-		line6->buffer_message = kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL);
+		line6->buffer_message = kmalloc(LINE6_MIDI_MESSAGE_MAXLEN, GFP_KERNEL);
 		if (!line6->buffer_message)
 			return -ENOMEM;
+	} else {
+		ret = line6_hwdep_init(line6);
+		if (ret < 0)
+			return ret;
 	}
 
 	ret = line6_start_listen(line6);
@@ -716,3 +858,4 @@ EXPORT_SYMBOL_GPL(line6_resume);
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
+
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index 14093c5..207a1d5 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -12,8 +12,9 @@
 #ifndef DRIVER_H
 #define DRIVER_H
 
-#include <linux/spinlock.h>
 #include <linux/usb.h>
+#include <linux/mutex.h>
+#include <linux/kfifo.h>
 #include <sound/core.h>
 
 #include "midi.h"
@@ -32,7 +33,16 @@
 
 #define LINE6_TIMEOUT 1
 #define LINE6_BUFSIZE_LISTEN 64
-#define LINE6_MESSAGE_MAXLEN 256
+#define LINE6_MIDI_MESSAGE_MAXLEN 256
+
+#define LINE6_RAW_MESSAGES_MAXCOUNT_ORDER 7
+/* 4k packets are common, BUFSIZE * MAXCOUNT should be bigger... */
+#define LINE6_RAW_MESSAGES_MAXCOUNT (1 << LINE6_RAW_MESSAGES_MAXCOUNT_ORDER)
+
+
+#if LINE6_BUFSIZE_LISTEN > 65535
+#error "Use dynamic fifo instead"
+#endif
 
 /*
 	Line 6 MIDI control commands
@@ -156,6 +166,15 @@ struct usb_line6 {
 	/* Length of message to be processed, generated from MIDI layer  */
 	int message_length;
 
+	/* Circular buffer for non-MIDI control messages */
+	struct {
+		struct mutex read_lock;
+		wait_queue_head_t wait_queue;
+		int active:1;
+		STRUCT_KFIFO_REC_2(LINE6_BUFSIZE_LISTEN * LINE6_RAW_MESSAGES_MAXCOUNT)
+			fifo;
+	} messages;
+
 	/* If MIDI is supported, buffer_message contains the pre-processed data;
 	 * otherwise the data is only in urb_listen (buffer_incoming). */
 	void (*process_message)(struct usb_line6 *);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* Re: [PATCH v2 1/9] ALSA: line6: Make driver configuration more generic.
  2016-08-18 22:20   ` [PATCH v2 1/9] ALSA: line6: Make driver configuration more generic Andrej Krutak
@ 2016-08-24 14:50     ` Takashi Iwai
  0 siblings, 0 replies; 81+ messages in thread
From: Takashi Iwai @ 2016-08-24 14:50 UTC (permalink / raw)
  To: Andrej Krutak; +Cc: stefanha, alsa-devel, grabner

On Fri, 19 Aug 2016 00:20:31 +0200,
Andrej Krutak wrote:
> 
> The main reasons are different settings for USB low/high speed and possible
> different channel counts for in/out; required by POD X3.
> 
> This consists of two related parts:
> 
> Support for high-speed USB:
> * USB_INTERVALS_PER_SECOND -> LOW/HIGH settings
>   (high needs 8000, instead of 1000)
> * LINE6_ISO_BUFFERS -> iso_buffers (count of iso buffers depends on
>   USB speed, 2 is not enough for high speed)
> 
> Support for assymetrical in/out configurations:
> * bytes_per_frame -> bytes_per_channel
> * max_packet_size -> max_packet_size_in/out

It'd be better to split into a few more patches.  The changes about
making iso_buffers dynamic and others are basically independent.

So, as the first patch, just convert to the dynamic urbs allocation
and adding line6->iso_buffers field, as a preliminary change.
Then introduce the two speed modes and align iso_buffers in the next
patch.  And at last, convert bytes_per_frame and max_packet_size.
They can be either one or two individual patches, too.

Also, just a nitpick:

> +	index = find_first_zero_bit(
> +		&line6pcm->in.active_urbs, line6pcm->line6->iso_buffers);

This style (leaving the open parenthesis) isn't common in the kernel
code.


thanks,

Takashi

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH v2 2/9] ALSA: line6: Add LINE6_CAP_IN_NEEDS_OUT, a void playback stream during capture
  2016-08-18 22:20   ` [PATCH v2 2/9] ALSA: line6: Add LINE6_CAP_IN_NEEDS_OUT, a void playback stream during capture Andrej Krutak
@ 2016-08-24 14:53     ` Takashi Iwai
  0 siblings, 0 replies; 81+ messages in thread
From: Takashi Iwai @ 2016-08-24 14:53 UTC (permalink / raw)
  To: Andrej Krutak; +Cc: stefanha, alsa-devel, grabner

On Fri, 19 Aug 2016 00:20:32 +0200,
Andrej Krutak wrote:
> 
> E.g. POD X3 seems to require playback data to be sent to it to generate
> capture data. Otherwise the device stalls and doesn't send any more capture
> data until it's reset.
> 
> Signed-off-by: Andrej Krutak <dev@andree.sk>

Only minor issues below:

> diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
> index 2d32139..69658dc 100644
> --- a/sound/usb/line6/driver.h
> +++ b/sound/usb/line6/driver.h
> @@ -100,8 +100,10 @@ enum {
>  	LINE6_CAP_CONTROL =	1 << 0,
>  	/* device supports PCM input/output via USB */
>  	LINE6_CAP_PCM =		1 << 1,
> -	/* device support hardware monitoring */
> +	/* device supports hardware monitoring */
>  	LINE6_CAP_HWMON =	1 << 2,
> +	/* device requires output data when input is read */
> +	LINE6_CAP_IN_NEEDS_OUT = 1 << 4,

Any reason to skip 3?


> @@ -242,6 +242,15 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
>  		switch (cmd) {
>  		case SNDRV_PCM_TRIGGER_START:
>  		case SNDRV_PCM_TRIGGER_RESUME:
> +			if ((s->stream == SNDRV_PCM_STREAM_CAPTURE) &&

The parentheses are superfluous around (a == b).

> +				(line6pcm->line6->properties->capabilities &
> +					LINE6_CAP_IN_NEEDS_OUT)
> +			) {

Move the close parenthesis and the brace to the previous line.


> @@ -250,6 +259,13 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
>  
>  		case SNDRV_PCM_TRIGGER_STOP:
>  		case SNDRV_PCM_TRIGGER_SUSPEND:
> +			if ((s->stream == SNDRV_PCM_STREAM_CAPTURE) &&
> +				(line6pcm->line6->properties->capabilities &
> +					LINE6_CAP_IN_NEEDS_OUT)
> +			) {
> +				line6_stream_stop(line6pcm, SNDRV_PCM_STREAM_PLAYBACK,
> +					  LINE6_STREAM_CAPTURE_HELPER);
> +			}

Ditto.


thanks,

Takashi

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH v2 3/9] ALSA: line6: Distinguish device init (ctrl EP) and MIDI data transfer (int EP)
  2016-08-18 22:20   ` [PATCH v2 3/9] ALSA: line6: Distinguish device init (ctrl EP) and MIDI data transfer (int EP) Andrej Krutak
@ 2016-08-24 14:56     ` Takashi Iwai
  0 siblings, 0 replies; 81+ messages in thread
From: Takashi Iwai @ 2016-08-24 14:56 UTC (permalink / raw)
  To: Andrej Krutak; +Cc: stefanha, alsa-devel, grabner

On Fri, 19 Aug 2016 00:20:33 +0200,
Andrej Krutak wrote:
> 
> diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
> index 69658dc..7aeb6ad 100644
> --- a/sound/usb/line6/driver.h
> +++ b/sound/usb/line6/driver.h
> @@ -102,6 +102,8 @@ enum {
>  	LINE6_CAP_PCM =		1 << 1,
>  	/* device supports hardware monitoring */
>  	LINE6_CAP_HWMON =	1 << 2,
> +	/* device uses raw MIDI via USB (data endpoints) */
> +	LINE6_CAP_CONTROL_MIDI = 1 << 3,
>  	/* device requires output data when input is read */
>  	LINE6_CAP_IN_NEEDS_OUT = 1 << 4,

OK, now I see the reason of the disorder in the previous patch.
But then align either the patch order or the bit order.


Takashi

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH v2 6/9] ALSA: line6: Allow bulk endpoints instead of interrupt endpoints
  2016-08-18 22:20   ` [PATCH v2 6/9] ALSA: line6: Allow bulk endpoints instead of interrupt endpoints Andrej Krutak
@ 2016-08-24 15:02     ` Takashi Iwai
  0 siblings, 0 replies; 81+ messages in thread
From: Takashi Iwai @ 2016-08-24 15:02 UTC (permalink / raw)
  To: Andrej Krutak; +Cc: stefanha, alsa-devel, grabner

On Fri, 19 Aug 2016 00:20:36 +0200,
Andrej Krutak wrote:
> 
> Some PODs (e.g. POD X3) have bulk instead of interrupt endpoints for
> data transfer.

This patch should be applied before the actual usage of POD X3, so it
should be patch 4.


Takashi

> 
> Signed-off-by: Andrej Krutak <dev@andree.sk>
> ---
>  sound/usb/line6/driver.c | 63 ++++++++++++++++++++++++++++++++++++------------
>  1 file changed, 48 insertions(+), 15 deletions(-)
> 
> diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
> index 5fd6cad..9b16777 100644
> --- a/sound/usb/line6/driver.c
> +++ b/sound/usb/line6/driver.c
> @@ -66,10 +66,17 @@ static int line6_start_listen(struct usb_line6 *line6)
>  {
>  	int err;
>  
> -	usb_fill_int_urb(line6->urb_listen, line6->usbdev,
> -		usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r),
> -		line6->buffer_listen, LINE6_BUFSIZE_LISTEN,
> -		line6_data_received, line6, line6->interval);
> +	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
> +		usb_fill_int_urb(line6->urb_listen, line6->usbdev,
> +			usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r),
> +			line6->buffer_listen, LINE6_BUFSIZE_LISTEN,
> +			line6_data_received, line6, line6->interval);
> +	} else {
> +		usb_fill_bulk_urb(line6->urb_listen, line6->usbdev,
> +			usb_rcvbulkpipe(line6->usbdev, line6->properties->ep_ctrl_r),
> +			line6->buffer_listen, LINE6_BUFSIZE_LISTEN,
> +			line6_data_received, line6);
> +	}
>  	line6->urb_listen->actual_length = 0;
>  	err = usb_submit_urb(line6->urb_listen, GFP_ATOMIC);
>  	return err;
> @@ -90,6 +97,7 @@ static int line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
>  				  int size)
>  {
>  	int i, done = 0;
> +	const struct line6_properties *properties = line6->properties;
>  
>  	for (i = 0; i < size; i += line6->max_packet_size) {
>  		int partial;
> @@ -97,15 +105,21 @@ static int line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
>  		int frag_size = min(line6->max_packet_size, size - i);
>  		int retval;
>  
> -		retval = usb_interrupt_msg(line6->usbdev,
> -					usb_sndintpipe(line6->usbdev,
> -						line6->properties->ep_ctrl_w),
> -					(char *)frag_buf, frag_size,
> -					&partial, LINE6_TIMEOUT * HZ);
> +		if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
> +			retval = usb_interrupt_msg(line6->usbdev,
> +						usb_sndintpipe(line6->usbdev, properties->ep_ctrl_w),
> +						(char *)frag_buf, frag_size,
> +						&partial, LINE6_TIMEOUT * HZ);
> +		} else {
> +			retval = usb_bulk_msg(line6->usbdev,
> +						usb_sndbulkpipe(line6->usbdev, properties->ep_ctrl_w),
> +						(char *)frag_buf, frag_size,
> +						&partial, LINE6_TIMEOUT * HZ);
> +		}
>  
>  		if (retval) {
>  			dev_err(line6->ifcdev,
> -				"usb_interrupt_msg failed (%d)\n", retval);
> +				"usb_bulk_msg failed (%d)\n", retval);
>  			break;
>  		}
>  
> @@ -140,10 +154,17 @@ static int line6_send_raw_message_async_part(struct message *msg,
>  	int done = msg->done;
>  	int bytes = min(msg->size - done, line6->max_packet_size);
>  
> -	usb_fill_int_urb(urb, line6->usbdev,
> -		usb_sndintpipe(line6->usbdev, line6->properties->ep_ctrl_w),
> -		(char *)msg->buffer + done, bytes,
> -		line6_async_request_sent, msg, line6->interval);
> +	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
> +		usb_fill_int_urb(urb, line6->usbdev,
> +			usb_sndintpipe(line6->usbdev, line6->properties->ep_ctrl_w),
> +			(char *)msg->buffer + done, bytes,
> +			line6_async_request_sent, msg, line6->interval);
> +	} else {
> +		usb_fill_bulk_urb(urb, line6->usbdev,
> +			usb_sndbulkpipe(line6->usbdev, line6->properties->ep_ctrl_w),
> +			(char *)msg->buffer + done, bytes,
> +			line6_async_request_sent, msg);
> +	}
>  
>  	msg->done += bytes;
>  	retval = usb_submit_urb(urb, GFP_ATOMIC);
> @@ -462,7 +483,19 @@ static void line6_destruct(struct snd_card *card)
>  static void line6_get_interval(struct usb_line6 *line6)
>  {
>  	struct usb_device *usbdev = line6->usbdev;
> -	struct usb_host_endpoint *ep = usbdev->ep_in[line6->properties->ep_ctrl_r];
> +	const struct line6_properties *properties = line6->properties;
> +	int pipe;
> +	struct usb_host_endpoint *ep;
> +
> +	if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
> +		pipe =
> +			usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r);
> +	} else {
> +		pipe =
> +			usb_rcvbulkpipe(line6->usbdev, line6->properties->ep_ctrl_r);
> +	}
> +	ep = usbdev->ep_in[usb_pipeendpoint(pipe)];
> +
>  	if (ep) {
>  		line6->interval = ep->desc.bInterval;
>  		if (usbdev->speed == USB_SPEED_LOW) {
> -- 
> 1.9.1
> 
> 

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH v2 7/9] ALSA: line6: Allow processing of raw incoming messages
  2016-08-18 22:20   ` [PATCH v2 7/9] ALSA: line6: Allow processing of raw incoming messages Andrej Krutak
@ 2016-08-24 15:05     ` Takashi Iwai
  2016-08-30 14:35       ` Andrej Kruták
  0 siblings, 1 reply; 81+ messages in thread
From: Takashi Iwai @ 2016-08-24 15:05 UTC (permalink / raw)
  To: Andrej Krutak; +Cc: stefanha, alsa-devel, grabner

On Fri, 19 Aug 2016 00:20:37 +0200,
Andrej Krutak wrote:
> 
> Not all PODs use MIDI via USB data interface, thus allow avoiding
> that code and instead using direct processing.
> 
> Signed-off-by: Andrej Krutak <dev@andree.sk>
> ---
>  sound/usb/line6/driver.c | 59 ++++++++++++++++++++++++++++--------------------
>  sound/usb/line6/driver.h |  8 ++++---
>  sound/usb/line6/midi.c   |  2 +-
>  3 files changed, 40 insertions(+), 29 deletions(-)
> 
> diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
> index 9b16777..853a143 100644
> --- a/sound/usb/line6/driver.c
> +++ b/sound/usb/line6/driver.c
> @@ -290,26 +290,31 @@ static void line6_data_received(struct urb *urb)
>  	if (urb->status == -ESHUTDOWN)
>  		return;
>  
> -	done =
> -	    line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length);
> +	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
> +		done =
> +			line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length);
>  
> -	if (done < urb->actual_length) {
> -		line6_midibuf_ignore(mb, done);
> -		dev_dbg(line6->ifcdev, "%d %d buffer overflow - message skipped\n",
> -			done, urb->actual_length);
> -	}
> +		if (done < urb->actual_length) {
> +			line6_midibuf_ignore(mb, done);
> +			dev_dbg(line6->ifcdev, "%d %d buffer overflow - message skipped\n",
> +				done, urb->actual_length);
> +		}
>  
> -	for (;;) {
> -		done =
> -		    line6_midibuf_read(mb, line6->buffer_message,
> -				       LINE6_MESSAGE_MAXLEN);
> +		for (;;) {
> +			done =
> +				line6_midibuf_read(mb, line6->buffer_message,
> +						LINE6_MESSAGE_MAXLEN);
>  
> -		if (done == 0)
> -			break;
> +			if (done == 0)
> +				break;
>  
> -		line6->message_length = done;
> -		line6_midi_receive(line6, line6->buffer_message, done);
> +			line6->message_length = done;
> +			line6_midi_receive(line6, line6->buffer_message, done);
>  
> +			if (line6->process_message)
> +				line6->process_message(line6);
> +		}
> +	} else {
>  		if (line6->process_message)
>  			line6->process_message(line6);
>  	}

Both if and else run the same code (line6->process_message) here at
the end.  That is, this can be outside the if block.

Also, the patch should be also before the actual usage, i.e. patch 5.


Takashi

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH v2 8/9] ALSA: line6: Cleanup initialization
  2016-08-18 22:20   ` [PATCH v2 8/9] ALSA: line6: Cleanup initialization Andrej Krutak
@ 2016-08-24 15:06     ` Takashi Iwai
  0 siblings, 0 replies; 81+ messages in thread
From: Takashi Iwai @ 2016-08-24 15:06 UTC (permalink / raw)
  To: Andrej Krutak; +Cc: stefanha, alsa-devel, grabner

On Fri, 19 Aug 2016 00:20:38 +0200,
Andrej Krutak wrote:
> 
> Only determine control port properties if the devices needs it.
> Only initialize PCM for POD HD devices that support it.
> No POD HD seems to support MIDI, thus drop the initialization.

This should be split to two individual patches.  They are irrelevant
with each other at all.


Takashi

> 
> Signed-off-by: Andrej Krutak <dev@andree.sk>
> ---
>  sound/usb/line6/driver.c |  3 +--
>  sound/usb/line6/podhd.c  | 19 ++++++++-----------
>  2 files changed, 9 insertions(+), 13 deletions(-)
> 
> diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
> index 853a143..8a71d45 100644
> --- a/sound/usb/line6/driver.c
> +++ b/sound/usb/line6/driver.c
> @@ -612,9 +612,8 @@ int line6_probe(struct usb_interface *interface,
>  		goto error;
>  	}
>  
> -	line6_get_interval(line6);
> -
>  	if (properties->capabilities & LINE6_CAP_CONTROL) {
> +		line6_get_interval(line6);
>  		ret = line6_init_cap_control(line6);
>  		if (ret < 0)
>  			goto error;
> diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c
> index 8246ea5..193eb29 100644
> --- a/sound/usb/line6/podhd.c
> +++ b/sound/usb/line6/podhd.c
> @@ -314,17 +314,14 @@ static int podhd_init(struct usb_line6 *line6,
>  			return err;
>  	}
>  
> -	/* initialize MIDI subsystem: */
> -	err = line6_init_midi(line6);
> -	if (err < 0)
> -		return err;
> -
> -	/* initialize PCM subsystem: */
> -	err = line6_init_pcm(line6,
> -		(id->driver_info == LINE6_PODX3) ? &podx3_pcm_properties :
> -		&podhd_pcm_properties);
> -	if (err < 0)
> -		return err;
> +	if (pod->line6.properties->capabilities & LINE6_CAP_PCM) {
> +		/* initialize PCM subsystem: */
> +		err = line6_init_pcm(line6,
> +			(id->driver_info == LINE6_PODX3) ? &podx3_pcm_properties :
> +			&podhd_pcm_properties);
> +		if (err < 0)
> +			return err;
> +	}
>  
>  	if (!(pod->line6.properties->capabilities & LINE6_CAP_CONTROL)) {
>  		/* register USB audio system directly */
> -- 
> 1.9.1
> 
> 

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH v2 7/9] ALSA: line6: Allow processing of raw incoming messages
  2016-08-24 15:05     ` Takashi Iwai
@ 2016-08-30 14:35       ` Andrej Kruták
  0 siblings, 0 replies; 81+ messages in thread
From: Andrej Kruták @ 2016-08-30 14:35 UTC (permalink / raw)
  To: Takashi Iwai, alsa-devel, Stefan Hajnoczi, grabner, perex

>> -     for (;;) {
>> -             done =
>> -                 line6_midibuf_read(mb, line6->buffer_message,
>> -                                    LINE6_MESSAGE_MAXLEN);
>> +             for (;;) {
>> +                     done =
>> +                             line6_midibuf_read(mb, line6->buffer_message,
>> +                                             LINE6_MESSAGE_MAXLEN);
>>
>> -             if (done == 0)
>> -                     break;
>> +                     if (done == 0)
>> +                             break;
>>
>> -             line6->message_length = done;
>> -             line6_midi_receive(line6, line6->buffer_message, done);
>> +                     line6->message_length = done;
>> +                     line6_midi_receive(line6, line6->buffer_message, done);
>>
>> +                     if (line6->process_message)
>> +                             line6->process_message(line6);
>> +             }
>> +     } else {
>>               if (line6->process_message)
>>                       line6->process_message(line6);
>>       }
>
> Both if and else run the same code (line6->process_message) here at
> the end.  That is, this can be outside the if block.
>

Nope, the first one is done in a loop...

> Also, the patch should be also before the actual usage, i.e. patch 5.
>

Ack.


-- 
Andrej

^ permalink raw reply	[flat|nested] 81+ messages in thread

* [PATCH v3 00/12] Line6 POD X3/X3Live suport
  2016-08-11 19:02 [PATCH 00/15] Line6 POD X3/X3Live suport Andrej Krutak
                   ` (16 preceding siblings ...)
  2016-08-18 22:20 ` [PATCH v2 0/9] " Andrej Krutak
@ 2016-09-16  9:12 ` Andrej Krutak
  2016-09-16  9:12   ` [PATCH v3 01/12] ALSA: line6: Enable different number of URBs for frame transfers Andrej Krutak
                     ` (11 more replies)
  2016-09-18 18:59 ` [PATCH v4 00/12] Line6 POD X3/X3Live suport Andrej Krutak
  18 siblings, 12 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-09-16  9:12 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

Hello all,

attached are the yet again reworked patches. Basically the diff is only some formatting. In addition, Some of the previously big patches were split to multiple smaller ones (but I didn't chceck that after each one, the compilation works and the result doesn't segfault etc.)

Greetings!

Andrej Krutak (12):
  ALSA: line6: Enable different number of URBs for frame transfers
  ALSA: line6: Add high-speed USB support
  ALSA: line6: Support assymetrical in/out configurations
  ALSA: line6: Allow different channel numbers for in/out
  ALSA: line6: Add LINE6_CAP_IN_NEEDS_OUT, a void playback stream during
    capture
  ALSA: line6: Distinguish device init (ctrl EP) and MIDI data transfer
    (int EP)
  ALSA: line6: Allow processing of raw incoming messages
  ALSA: line6: Add support for POD X3
  ALSA: line6: Add support for POD X3 Live (only USB ID differs from POD
    X3)
  ALSA: line6: Only determine control port properties if needed
  ALSA: line6: Cleanup podhd initialization
  ALSA: line6: Add hwdep interface to access the POD control messages

 include/uapi/sound/asound.h |   3 +-
 sound/usb/line6/Kconfig     |   4 +-
 sound/usb/line6/capture.c   |  50 +++++---
 sound/usb/line6/driver.c    | 269 +++++++++++++++++++++++++++++++++------
 sound/usb/line6/driver.h    |  58 +++++++--
 sound/usb/line6/midi.c      |   2 +-
 sound/usb/line6/pcm.c       |  82 ++++++++----
 sound/usb/line6/pcm.h       |  19 +--
 sound/usb/line6/playback.c  |  37 ++++--
 sound/usb/line6/pod.c       |  12 +-
 sound/usb/line6/podhd.c     | 300 ++++++++++++++++++++++++++++++++++++++++----
 sound/usb/line6/toneport.c  |   6 +-
 sound/usb/line6/variax.c    |   6 +-
 13 files changed, 702 insertions(+), 146 deletions(-)

-- 
1.9.1

^ permalink raw reply	[flat|nested] 81+ messages in thread

* [PATCH v3 01/12] ALSA: line6: Enable different number of URBs for frame transfers
  2016-09-16  9:12 ` [PATCH v3 00/12] Line6 POD X3/X3Live suport Andrej Krutak
@ 2016-09-16  9:12   ` Andrej Krutak
  2016-09-16  9:12   ` [PATCH v3 02/12] ALSA: line6: Add high-speed USB support Andrej Krutak
                     ` (10 subsequent siblings)
  11 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-09-16  9:12 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

This basically changes LINE6_ISO_BUFFERS constant to a configurable
iso_buffers property.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/capture.c  | 17 +++++++++++------
 sound/usb/line6/driver.c   |  1 +
 sound/usb/line6/driver.h   |  2 ++
 sound/usb/line6/pcm.c      | 23 +++++++++++++++--------
 sound/usb/line6/pcm.h      |  2 +-
 sound/usb/line6/playback.c | 19 ++++++++++++-------
 6 files changed, 42 insertions(+), 22 deletions(-)

diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c
index f518fbb..e20a6bd 100644
--- a/sound/usb/line6/capture.c
+++ b/sound/usb/line6/capture.c
@@ -29,10 +29,10 @@ static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm)
 	int ret;
 	struct urb *urb_in;
 
-	index =
-	    find_first_zero_bit(&line6pcm->in.active_urbs, LINE6_ISO_BUFFERS);
+	index = find_first_zero_bit(&line6pcm->in.active_urbs,
+	                            line6pcm->line6->iso_buffers);
 
-	if (index < 0 || index >= LINE6_ISO_BUFFERS) {
+	if (index < 0 || index >= line6pcm->line6->iso_buffers) {
 		dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
 		return -EINVAL;
 	}
@@ -73,7 +73,7 @@ int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm)
 {
 	int ret = 0, i;
 
-	for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
+	for (i = 0; i < line6pcm->line6->iso_buffers; ++i) {
 		ret = submit_audio_in_urb(line6pcm);
 		if (ret < 0)
 			break;
@@ -154,7 +154,7 @@ static void audio_in_callback(struct urb *urb)
 	line6pcm->in.last_frame = urb->start_frame;
 
 	/* find index of URB */
-	for (index = 0; index < LINE6_ISO_BUFFERS; ++index)
+	for (index = 0; index < line6pcm->line6->iso_buffers; ++index)
 		if (urb == line6pcm->in.urbs[index])
 			break;
 
@@ -247,8 +247,13 @@ int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm)
 	struct usb_line6 *line6 = line6pcm->line6;
 	int i;
 
+	line6pcm->in.urbs = kzalloc(
+		sizeof(struct urb *) * line6->iso_buffers, GFP_KERNEL);
+	if (line6pcm->in.urbs == NULL)
+		return -ENOMEM;
+
 	/* create audio URBs and fill in constant values: */
-	for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
+	for (i = 0; i < line6->iso_buffers; ++i) {
 		struct urb *urb;
 
 		/* URB for audio in: */
diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 81b7da8..527c408 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -467,6 +467,7 @@ static void line6_get_interval(struct usb_line6 *line6)
 	unsigned epnum = usb_pipeendpoint(pipe);
 
 	ep = usbdev->ep_in[epnum];
+	line6->iso_buffers = LINE6_ISO_BUFFERS;
 	if (ep) {
 		line6->interval = ep->desc.bInterval;
 		line6->max_packet_size = le16_to_cpu(ep->desc.wMaxPacketSize);
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index 7da643e..43dd1d0 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -111,6 +111,8 @@ struct usb_line6 {
 
 	/* Interval (ms) */
 	int interval;
+	/* Number of isochronous URBs used for frame transfers */
+	int iso_buffers;
 
 	/* Maximum size of USB packet */
 	int max_packet_size;
diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c
index 41aa335..6c9b7cf 100644
--- a/sound/usb/line6/pcm.c
+++ b/sound/usb/line6/pcm.c
@@ -104,7 +104,7 @@ static void line6_unlink_audio_urbs(struct snd_line6_pcm *line6pcm,
 {
 	int i;
 
-	for (i = 0; i < LINE6_ISO_BUFFERS; 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]);
@@ -124,7 +124,7 @@ static void line6_wait_clear_audio_urbs(struct snd_line6_pcm *line6pcm,
 
 	do {
 		alive = 0;
-		for (i = 0; i < LINE6_ISO_BUFFERS; i++) {
+		for (i = 0; i < line6pcm->line6->iso_buffers; i++) {
 			if (test_bit(i, &pcms->active_urbs))
 				alive++;
 		}
@@ -153,8 +153,9 @@ static int line6_buffer_acquire(struct snd_line6_pcm *line6pcm,
 {
 	/* 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);
+		pstr->buffer = kmalloc(line6pcm->line6->iso_buffers *
+                                      LINE6_ISO_PACKETS *
+                                      line6pcm->max_packet_size, GFP_KERNEL);
 		if (!pstr->buffer)
 			return -ENOMEM;
 	}
@@ -434,24 +435,30 @@ static struct snd_kcontrol_new line6_controls[] = {
 /*
 	Cleanup the PCM device.
 */
-static void cleanup_urbs(struct line6_pcm_stream *pcms)
+static void cleanup_urbs(struct line6_pcm_stream *pcms, int iso_buffers)
 {
 	int i;
 
-	for (i = 0; i < LINE6_ISO_BUFFERS; i++) {
+	/* Most likely impossible in current code... */
+	if (pcms->urbs == NULL)
+		return;
+
+	for (i = 0; i < iso_buffers; i++) {
 		if (pcms->urbs[i]) {
 			usb_kill_urb(pcms->urbs[i]);
 			usb_free_urb(pcms->urbs[i]);
 		}
 	}
+	kfree(pcms->urbs);
+	pcms->urbs = NULL;
 }
 
 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);
+	cleanup_urbs(&line6pcm->out, line6pcm->line6->iso_buffers);
+	cleanup_urbs(&line6pcm->in, line6pcm->line6->iso_buffers);
 	kfree(line6pcm);
 }
 
diff --git a/sound/usb/line6/pcm.h b/sound/usb/line6/pcm.h
index 508410a..e983880 100644
--- a/sound/usb/line6/pcm.h
+++ b/sound/usb/line6/pcm.h
@@ -90,7 +90,7 @@ struct line6_pcm_properties {
 
 struct line6_pcm_stream {
 	/* allocated URBs */
-	struct urb *urbs[LINE6_ISO_BUFFERS];
+	struct urb **urbs;
 
 	/* Temporary buffer;
 	 * Since the packet size is not known in advance, this buffer is
diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c
index 97ed593..7a52806 100644
--- a/sound/usb/line6/playback.c
+++ b/sound/usb/line6/playback.c
@@ -154,10 +154,10 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
 		(USB_INTERVALS_PER_SECOND / LINE6_ISO_INTERVAL);
 	struct urb *urb_out;
 
-	index =
-	    find_first_zero_bit(&line6pcm->out.active_urbs, LINE6_ISO_BUFFERS);
+	index = find_first_zero_bit(&line6pcm->out.active_urbs,
+	                            line6pcm->line6->iso_buffers);
 
-	if (index < 0 || index >= LINE6_ISO_BUFFERS) {
+	if (index < 0 || index >= line6pcm->line6->iso_buffers) {
 		dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
 		return -EINVAL;
 	}
@@ -286,7 +286,7 @@ int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm)
 {
 	int ret = 0, i;
 
-	for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
+	for (i = 0; i < line6pcm->line6->iso_buffers; ++i) {
 		ret = submit_audio_out_urb(line6pcm);
 		if (ret < 0)
 			break;
@@ -313,11 +313,11 @@ static void audio_out_callback(struct urb *urb)
 	line6pcm->out.last_frame = urb->start_frame;
 
 	/* find index of URB */
-	for (index = 0; index < LINE6_ISO_BUFFERS; index++)
+	for (index = 0; index < line6pcm->line6->iso_buffers; index++)
 		if (urb == line6pcm->out.urbs[index])
 			break;
 
-	if (index >= LINE6_ISO_BUFFERS)
+	if (index >= line6pcm->line6->iso_buffers)
 		return;		/* URB has been unlinked asynchronously */
 
 	for (i = 0; i < LINE6_ISO_PACKETS; i++)
@@ -401,8 +401,13 @@ int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm)
 	struct usb_line6 *line6 = line6pcm->line6;
 	int i;
 
+	line6pcm->out.urbs = kzalloc(
+		sizeof(struct urb *) * line6->iso_buffers, GFP_KERNEL);
+	if (line6pcm->out.urbs == NULL)
+		return -ENOMEM;
+
 	/* create audio URBs and fill in constant values: */
-	for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
+	for (i = 0; i < line6->iso_buffers; ++i) {
 		struct urb *urb;
 
 		/* URB for audio out: */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v3 02/12] ALSA: line6: Add high-speed USB support
  2016-09-16  9:12 ` [PATCH v3 00/12] Line6 POD X3/X3Live suport Andrej Krutak
  2016-09-16  9:12   ` [PATCH v3 01/12] ALSA: line6: Enable different number of URBs for frame transfers Andrej Krutak
@ 2016-09-16  9:12   ` Andrej Krutak
  2016-09-16  9:12   ` [PATCH v3 03/12] ALSA: line6: Support assymetrical in/out configurations Andrej Krutak
                     ` (9 subsequent siblings)
  11 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-09-16  9:12 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

This has two parts:
* intervals_per_second setup
  (high speed needs 8000, instead of 1000)
* iso_buffers setup (count of iso buffers depends on
  USB speed, 2 is not enough for high speed)

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/capture.c  | 10 +++++++++-
 sound/usb/line6/driver.c   | 15 ++++++++++-----
 sound/usb/line6/driver.h   | 15 ++++++++++++---
 sound/usb/line6/pcm.h      |  6 ++----
 sound/usb/line6/playback.c |  2 +-
 5 files changed, 34 insertions(+), 14 deletions(-)

diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c
index e20a6bd..44e9a8c 100644
--- a/sound/usb/line6/capture.c
+++ b/sound/usb/line6/capture.c
@@ -181,7 +181,15 @@ static void audio_in_callback(struct urb *urb)
 
 		length += fsize;
 
-		/* the following assumes LINE6_ISO_PACKETS == 1: */
+		BUILD_BUG_ON_MSG(LINE6_ISO_PACKETS != 1,
+			"The following code assumes LINE6_ISO_PACKETS == 1");
+		/* TODO:
+		 * Also, if iso_buffers != 2, the prev frame is almost random at
+		 * playback side.
+		 * This needs to be redesigned. It should be "stable", but we may
+		 * experience sync problems on such high-speed configs.
+		 */
+
 		line6pcm->prev_fbuf = fbuf;
 		line6pcm->prev_fsize = fsize;
 
diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 527c408..14032d9 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -462,14 +462,18 @@ static void line6_destruct(struct snd_card *card)
 static void line6_get_interval(struct usb_line6 *line6)
 {
 	struct usb_device *usbdev = line6->usbdev;
-	struct usb_host_endpoint *ep;
-	unsigned pipe = usb_rcvintpipe(usbdev, line6->properties->ep_ctrl_r);
-	unsigned epnum = usb_pipeendpoint(pipe);
+	struct usb_host_endpoint *ep = usbdev->ep_in[line6->properties->ep_ctrl_r];
 
-	ep = usbdev->ep_in[epnum];
-	line6->iso_buffers = LINE6_ISO_BUFFERS;
 	if (ep) {
 		line6->interval = ep->desc.bInterval;
+		if (usbdev->speed == USB_SPEED_LOW) {
+			line6->intervals_per_second = USB_LOW_INTERVALS_PER_SECOND;
+			line6->iso_buffers = USB_LOW_ISO_BUFFERS;
+		} else {
+			line6->intervals_per_second = USB_HIGH_INTERVALS_PER_SECOND;
+			line6->iso_buffers = USB_HIGH_ISO_BUFFERS;
+		}
+
 		line6->max_packet_size = le16_to_cpu(ep->desc.wMaxPacketSize);
 	} else {
 		dev_err(line6->ifcdev,
@@ -559,6 +563,7 @@ int line6_probe(struct usb_interface *interface,
 	/* query interface number */
 	interface_number = interface->cur_altsetting->desc.bInterfaceNumber;
 
+	/* TODO reserves the bus bandwidth even without actual transfer */
 	ret = usb_set_interface(usbdev, interface_number,
 				properties->altsetting);
 	if (ret < 0) {
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index 43dd1d0..a55eb88 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -18,7 +18,13 @@
 
 #include "midi.h"
 
-#define USB_INTERVALS_PER_SECOND 1000
+/* USB 1.1 speed configuration */
+#define USB_LOW_INTERVALS_PER_SECOND 1000
+#define USB_LOW_ISO_BUFFERS 2
+
+/* USB 2.0+ speed configuration */
+#define USB_HIGH_INTERVALS_PER_SECOND 8000
+#define USB_HIGH_ISO_BUFFERS 16
 
 /* Fallback USB interval and max packet size values */
 #define LINE6_FALLBACK_INTERVAL 10
@@ -109,12 +115,15 @@ struct usb_line6 {
 	/* Properties */
 	const struct line6_properties *properties;
 
-	/* Interval (ms) */
+	/* Interval for data USB packets */
 	int interval;
+	/* ...for isochronous transfers framing */
+	int intervals_per_second;
+
 	/* Number of isochronous URBs used for frame transfers */
 	int iso_buffers;
 
-	/* Maximum size of USB packet */
+	/* Maximum size of data USB packet */
 	int max_packet_size;
 
 	/* Device representing the USB interface */
diff --git a/sound/usb/line6/pcm.h b/sound/usb/line6/pcm.h
index e983880..38bf4df 100644
--- a/sound/usb/line6/pcm.h
+++ b/sound/usb/line6/pcm.h
@@ -20,9 +20,6 @@
 
 #include "driver.h"
 
-/* number of URBs */
-#define LINE6_ISO_BUFFERS	2
-
 /*
 	number of USB frames per URB
 	The Line 6 Windows driver always transmits two frames per packet, but
@@ -31,7 +28,8 @@
 */
 #define LINE6_ISO_PACKETS	1
 
-/* in a "full speed" device (such as the PODxt Pro) this means 1ms */
+/* in a "full speed" device (such as the PODxt Pro) this means 1ms,
+   for "high speed" it's 1/8ms */
 #define LINE6_ISO_INTERVAL	1
 
 #define LINE6_IMPULSE_DEFAULT_PERIOD 100
diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c
index 7a52806..d53aad1 100644
--- a/sound/usb/line6/playback.c
+++ b/sound/usb/line6/playback.c
@@ -151,7 +151,7 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
 		line6pcm->properties->rates.rats[0].num_min;
 	const int frame_factor =
 		line6pcm->properties->rates.rats[0].den *
-		(USB_INTERVALS_PER_SECOND / LINE6_ISO_INTERVAL);
+		(line6pcm->line6->intervals_per_second / LINE6_ISO_INTERVAL);
 	struct urb *urb_out;
 
 	index = find_first_zero_bit(&line6pcm->out.active_urbs,
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v3 03/12] ALSA: line6: Support assymetrical in/out configurations
  2016-09-16  9:12 ` [PATCH v3 00/12] Line6 POD X3/X3Live suport Andrej Krutak
  2016-09-16  9:12   ` [PATCH v3 01/12] ALSA: line6: Enable different number of URBs for frame transfers Andrej Krutak
  2016-09-16  9:12   ` [PATCH v3 02/12] ALSA: line6: Add high-speed USB support Andrej Krutak
@ 2016-09-16  9:12   ` Andrej Krutak
  2016-09-16 18:34     ` kbuild test robot
  2016-09-16  9:12   ` [PATCH v3 04/12] ALSA: line6: Allow different channel numbers for in/out Andrej Krutak
                     ` (8 subsequent siblings)
  11 siblings, 1 reply; 81+ messages in thread
From: Andrej Krutak @ 2016-09-16  9:12 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

Splits max_packet_size to max_packet_size_in/out (e.g. for
different channel counts).

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/capture.c  | 10 +++++-----
 sound/usb/line6/pcm.c      | 28 ++++++++++++++++------------
 sound/usb/line6/pcm.h      |  5 +++--
 sound/usb/line6/playback.c |  2 +-
 4 files changed, 25 insertions(+), 20 deletions(-)

diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c
index 44e9a8c..73cea26 100644
--- a/sound/usb/line6/capture.c
+++ b/sound/usb/line6/capture.c
@@ -44,13 +44,13 @@ static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm)
 		struct usb_iso_packet_descriptor *fin =
 		    &urb_in->iso_frame_desc[i];
 		fin->offset = urb_size;
-		fin->length = line6pcm->max_packet_size;
-		urb_size += line6pcm->max_packet_size;
+		fin->length = line6pcm->max_packet_size_in;
+		urb_size += line6pcm->max_packet_size_in;
 	}
 
 	urb_in->transfer_buffer =
 	    line6pcm->in.buffer +
-	    index * LINE6_ISO_PACKETS * line6pcm->max_packet_size;
+	    index * LINE6_ISO_PACKETS * line6pcm->max_packet_size_in;
 	urb_in->transfer_buffer_length = urb_size;
 	urb_in->context = line6pcm;
 
@@ -173,10 +173,10 @@ static void audio_in_callback(struct urb *urb)
 		fbuf = urb->transfer_buffer + fin->offset;
 		fsize = fin->actual_length;
 
-		if (fsize > line6pcm->max_packet_size) {
+		if (fsize > line6pcm->max_packet_size_in) {
 			dev_err(line6pcm->line6->ifcdev,
 				"driver and/or device bug: packet too large (%d > %d)\n",
-				fsize, line6pcm->max_packet_size);
+				fsize, line6pcm->max_packet_size_in);
 		}
 
 		length += fsize;
diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c
index 6c9b7cf..5fc1394 100644
--- a/sound/usb/line6/pcm.c
+++ b/sound/usb/line6/pcm.c
@@ -146,16 +146,20 @@ get_stream(struct snd_line6_pcm *line6pcm, int direction)
 }
 
 /* allocate a buffer if not opened yet;
- * call this in line6pcm.state_change mutex
+ * call this in line6pcm.state_mutex
  */
 static int line6_buffer_acquire(struct snd_line6_pcm *line6pcm,
-				struct line6_pcm_stream *pstr, int type)
+				struct line6_pcm_stream *pstr, int direction, int type)
 {
+	const int 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(line6pcm->line6->iso_buffers *
-                                      LINE6_ISO_PACKETS *
-                                      line6pcm->max_packet_size, GFP_KERNEL);
+				       LINE6_ISO_PACKETS * pkt_size, GFP_KERNEL);
 		if (!pstr->buffer)
 			return -ENOMEM;
 	}
@@ -163,12 +167,11 @@ static int line6_buffer_acquire(struct snd_line6_pcm *line6pcm,
 }
 
 /* free a buffer if all streams are closed;
- * call this in line6pcm.state_change mutex
+ * call this in line6pcm.state_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);
@@ -195,6 +198,7 @@ static int line6_stream_start(struct snd_line6_pcm *line6pcm, int direction,
 		else
 			ret = line6_submit_audio_in_all_urbs(line6pcm);
 	}
+
 	if (ret < 0)
 		clear_bit(type, &pstr->running);
 	spin_unlock_irqrestore(&pstr->lock, flags);
@@ -530,12 +534,12 @@ int line6_init_pcm(struct usb_line6 *line6,
 	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));
+	line6pcm->max_packet_size_in =
+		usb_maxpacket(line6->usbdev,
+			usb_rcvisocpipe(line6->usbdev, ep_read), 0);
+	line6pcm->max_packet_size_out =
+		usb_maxpacket(line6->usbdev,
+			usb_sndisocpipe(line6->usbdev, ep_write), 1);
 
 	spin_lock_init(&line6pcm->out.lock);
 	spin_lock_init(&line6pcm->in.lock);
diff --git a/sound/usb/line6/pcm.h b/sound/usb/line6/pcm.h
index 38bf4df..f408d15 100644
--- a/sound/usb/line6/pcm.h
+++ b/sound/usb/line6/pcm.h
@@ -155,11 +155,12 @@ struct snd_line6_pcm {
 	/* Previously captured frame (for software monitoring) */
 	unsigned char *prev_fbuf;
 
-	/* Size of previously captured frame (for software monitoring) */
+	/* Size of previously captured frame (for software monitoring/sync) */
 	int prev_fsize;
 
 	/* Maximum size of USB packet */
-	int max_packet_size;
+	int max_packet_size_in;
+	int max_packet_size_out;
 
 	/* PCM playback volume (left and right) */
 	int volume_playback[2];
diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c
index d53aad1..7b2644f 100644
--- a/sound/usb/line6/playback.c
+++ b/sound/usb/line6/playback.c
@@ -195,7 +195,7 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
 	urb_frames = urb_size / bytes_per_frame;
 	urb_out->transfer_buffer =
 	    line6pcm->out.buffer +
-	    index * LINE6_ISO_PACKETS * line6pcm->max_packet_size;
+	    index * LINE6_ISO_PACKETS * line6pcm->max_packet_size_out;
 	urb_out->transfer_buffer_length = urb_size;
 	urb_out->context = line6pcm;
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v3 04/12] ALSA: line6: Allow different channel numbers for in/out
  2016-09-16  9:12 ` [PATCH v3 00/12] Line6 POD X3/X3Live suport Andrej Krutak
                     ` (2 preceding siblings ...)
  2016-09-16  9:12   ` [PATCH v3 03/12] ALSA: line6: Support assymetrical in/out configurations Andrej Krutak
@ 2016-09-16  9:12   ` Andrej Krutak
  2016-09-16  9:12   ` [PATCH v3 05/12] ALSA: line6: Add LINE6_CAP_IN_NEEDS_OUT, a void playback stream during capture Andrej Krutak
                     ` (7 subsequent siblings)
  11 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-09-16  9:12 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

Changes bytes_per_frame to bytes_per_channel.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/capture.c  |  8 ++++++--
 sound/usb/line6/driver.h   |  2 +-
 sound/usb/line6/pcm.h      |  2 +-
 sound/usb/line6/playback.c | 14 +++++++++++---
 sound/usb/line6/pod.c      |  3 +--
 sound/usb/line6/podhd.c    |  4 +---
 sound/usb/line6/toneport.c |  2 +-
 7 files changed, 22 insertions(+), 13 deletions(-)

diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c
index 73cea26..7959aaa 100644
--- a/sound/usb/line6/capture.c
+++ b/sound/usb/line6/capture.c
@@ -90,7 +90,9 @@ void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize)
 	struct snd_pcm_substream *substream =
 	    get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE);
 	struct snd_pcm_runtime *runtime = substream->runtime;
-	const int bytes_per_frame = line6pcm->properties->bytes_per_frame;
+	const int bytes_per_frame =
+		line6pcm->properties->bytes_per_channel *
+		line6pcm->properties->capture_hw.channels_max;
 	int frames = fsize / bytes_per_frame;
 
 	if (runtime == NULL)
@@ -191,7 +193,9 @@ static void audio_in_callback(struct urb *urb)
 		 */
 
 		line6pcm->prev_fbuf = fbuf;
-		line6pcm->prev_fsize = fsize;
+		line6pcm->prev_fsize = fsize /
+			(line6pcm->properties->bytes_per_channel *
+			line6pcm->properties->capture_hw.channels_max);
 
 		if (!test_bit(LINE6_STREAM_IMPULSE, &line6pcm->in.running) &&
 		    test_bit(LINE6_STREAM_PCM, &line6pcm->in.running) &&
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index a55eb88..2d32139 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -31,7 +31,7 @@
 #define LINE6_FALLBACK_MAXPACKETSIZE 16
 
 #define LINE6_TIMEOUT 1
-#define LINE6_BUFSIZE_LISTEN 32
+#define LINE6_BUFSIZE_LISTEN 64
 #define LINE6_MESSAGE_MAXLEN 256
 
 /*
diff --git a/sound/usb/line6/pcm.h b/sound/usb/line6/pcm.h
index f408d15..58d36f9 100644
--- a/sound/usb/line6/pcm.h
+++ b/sound/usb/line6/pcm.h
@@ -83,7 +83,7 @@ enum {
 struct line6_pcm_properties {
 	struct snd_pcm_hardware playback_hw, capture_hw;
 	struct snd_pcm_hw_constraint_ratdens rates;
-	int bytes_per_frame;
+	int bytes_per_channel;
 };
 
 struct line6_pcm_stream {
diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c
index 7b2644f..bc5799c 100644
--- a/sound/usb/line6/playback.c
+++ b/sound/usb/line6/playback.c
@@ -146,7 +146,9 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
 	int index;
 	int i, urb_size, urb_frames;
 	int ret;
-	const int bytes_per_frame = line6pcm->properties->bytes_per_frame;
+	const int bytes_per_frame =
+		line6pcm->properties->bytes_per_channel *
+		line6pcm->properties->playback_hw.channels_max;
 	const int frame_increment =
 		line6pcm->properties->rates.rats[0].num_min;
 	const int frame_factor =
@@ -165,6 +167,7 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
 	urb_out = line6pcm->out.urbs[index];
 	urb_size = 0;
 
+	/* TODO: this may not work for LINE6_ISO_PACKETS != 1 */
 	for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
 		/* compute frame size for given sampling rate */
 		int fsize = 0;
@@ -178,9 +181,11 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
 			line6pcm->out.count += frame_increment;
 			n = line6pcm->out.count / frame_factor;
 			line6pcm->out.count -= n * frame_factor;
-			fsize = n * bytes_per_frame;
+			fsize = n;
 		}
 
+		fsize *= bytes_per_frame;
+
 		fout->offset = urb_size;
 		fout->length = fsize;
 		urb_size += fsize;
@@ -305,6 +310,9 @@ static void audio_out_callback(struct urb *urb)
 	struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context;
 	struct snd_pcm_substream *substream =
 	    get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK);
+	const int bytes_per_frame =
+		line6pcm->properties->bytes_per_channel *
+		line6pcm->properties->playback_hw.channels_max;
 
 #if USE_CLEAR_BUFFER_WORKAROUND
 	memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);
@@ -329,7 +337,7 @@ static void audio_out_callback(struct urb *urb)
 		struct snd_pcm_runtime *runtime = substream->runtime;
 
 		line6pcm->out.pos_done +=
-		    length / line6pcm->properties->bytes_per_frame;
+		    length / bytes_per_frame;
 
 		if (line6pcm->out.pos_done >= runtime->buffer_size)
 			line6pcm->out.pos_done -= runtime->buffer_size;
diff --git a/sound/usb/line6/pod.c b/sound/usb/line6/pod.c
index 45dd348..36e7274 100644
--- a/sound/usb/line6/pod.c
+++ b/sound/usb/line6/pod.c
@@ -83,7 +83,6 @@ struct usb_line6_pod {
 };
 
 #define POD_SYSEX_CODE 3
-#define POD_BYTES_PER_FRAME 6	/* 24bit audio (stereo) */
 
 /* *INDENT-OFF* */
 
@@ -167,7 +166,7 @@ static struct line6_pcm_properties pod_pcm_properties = {
 	.rates = {
 			    .nrats = 1,
 			    .rats = &pod_ratden},
-	.bytes_per_frame = POD_BYTES_PER_FRAME
+	.bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */
 };
 
 static const char pod_version_header[] = {
diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c
index 63dcaef..4fc4789 100644
--- a/sound/usb/line6/podhd.c
+++ b/sound/usb/line6/podhd.c
@@ -25,8 +25,6 @@ enum {
 	LINE6_PODHD500_1,
 };
 
-#define PODHD_BYTES_PER_FRAME 6	/* 24bit audio (stereo) */
-
 static struct snd_ratden podhd_ratden = {
 	.num_min = 48000,
 	.num_max = 48000,
@@ -73,7 +71,7 @@ static struct line6_pcm_properties podhd_pcm_properties = {
 	.rates = {
 			    .nrats = 1,
 			    .rats = &podhd_ratden},
-	.bytes_per_frame = PODHD_BYTES_PER_FRAME
+	.bytes_per_channel = 3 /* 24bit audio (stereo) */
 };
 
 /*
diff --git a/sound/usb/line6/toneport.c b/sound/usb/line6/toneport.c
index 6d4c50c..da76e03 100644
--- a/sound/usb/line6/toneport.c
+++ b/sound/usb/line6/toneport.c
@@ -114,7 +114,7 @@ static struct line6_pcm_properties toneport_pcm_properties = {
 	.rates = {
 			    .nrats = 1,
 			    .rats = &toneport_ratden},
-	.bytes_per_frame = 4
+	.bytes_per_channel = 2
 };
 
 static const struct {
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v3 05/12] ALSA: line6: Add LINE6_CAP_IN_NEEDS_OUT, a void playback stream during capture
  2016-09-16  9:12 ` [PATCH v3 00/12] Line6 POD X3/X3Live suport Andrej Krutak
                     ` (3 preceding siblings ...)
  2016-09-16  9:12   ` [PATCH v3 04/12] ALSA: line6: Allow different channel numbers for in/out Andrej Krutak
@ 2016-09-16  9:12   ` Andrej Krutak
  2016-09-16  9:12   ` [PATCH v3 06/12] ALSA: line6: Distinguish device init (ctrl EP) and MIDI data transfer (int EP) Andrej Krutak
                     ` (6 subsequent siblings)
  11 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-09-16  9:12 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

E.g. POD X3 seems to require playback data to be sent to it to generate
capture data. Otherwise the device stalls and doesn't send any more capture
data until it's reset.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/capture.c  |  5 +++++
 sound/usb/line6/driver.h   |  4 +++-
 sound/usb/line6/pcm.c      | 35 ++++++++++++++++++++++++++---------
 sound/usb/line6/pcm.h      |  4 +++-
 sound/usb/line6/toneport.c |  4 ++--
 5 files changed, 39 insertions(+), 13 deletions(-)

diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c
index 7959aaa..bf159c4 100644
--- a/sound/usb/line6/capture.c
+++ b/sound/usb/line6/capture.c
@@ -232,6 +232,8 @@ static int snd_line6_capture_open(struct snd_pcm_substream *substream)
 	if (err < 0)
 		return err;
 
+	line6_pcm_acquire(line6pcm, LINE6_STREAM_CAPTURE_HELPER, false);
+
 	runtime->hw = line6pcm->properties->capture_hw;
 	return 0;
 }
@@ -239,6 +241,9 @@ static int snd_line6_capture_open(struct snd_pcm_substream *substream)
 /* 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;
 }
 
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index 2d32139..0bcab38 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -100,8 +100,10 @@ enum {
 	LINE6_CAP_CONTROL =	1 << 0,
 	/* device supports PCM input/output via USB */
 	LINE6_CAP_PCM =		1 << 1,
-	/* device support hardware monitoring */
+	/* device supports hardware monitoring */
 	LINE6_CAP_HWMON =	1 << 2,
+	/* device requires output data when input is read */
+	LINE6_CAP_IN_NEEDS_OUT = 1 << 3,
 };
 
 /*
diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c
index 5fc1394..5f59993 100644
--- a/sound/usb/line6/pcm.c
+++ b/sound/usb/line6/pcm.c
@@ -52,7 +52,7 @@ static int snd_line6_impulse_volume_put(struct snd_kcontrol *kcontrol,
 
 	line6pcm->impulse_volume = value;
 	if (value > 0) {
-		err = line6_pcm_acquire(line6pcm, LINE6_STREAM_IMPULSE);
+		err = line6_pcm_acquire(line6pcm, LINE6_STREAM_IMPULSE, true);
 		if (err < 0) {
 			line6pcm->impulse_volume = 0;
 			return err;
@@ -242,6 +242,14 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
 		switch (cmd) {
 		case SNDRV_PCM_TRIGGER_START:
 		case SNDRV_PCM_TRIGGER_RESUME:
+			if (s->stream == SNDRV_PCM_STREAM_CAPTURE &&
+				(line6pcm->line6->properties->capabilities &
+					LINE6_CAP_IN_NEEDS_OUT)) {
+				err = line6_stream_start(line6pcm, SNDRV_PCM_STREAM_PLAYBACK,
+						 LINE6_STREAM_CAPTURE_HELPER);
+				if (err < 0)
+					return err;
+			}
 			err = line6_stream_start(line6pcm, s->stream,
 						 LINE6_STREAM_PCM);
 			if (err < 0)
@@ -250,6 +258,12 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
 
 		case SNDRV_PCM_TRIGGER_STOP:
 		case SNDRV_PCM_TRIGGER_SUSPEND:
+			if (s->stream == SNDRV_PCM_STREAM_CAPTURE &&
+				(line6pcm->line6->properties->capabilities &
+					LINE6_CAP_IN_NEEDS_OUT)) {
+				line6_stream_stop(line6pcm, SNDRV_PCM_STREAM_PLAYBACK,
+					  LINE6_STREAM_CAPTURE_HELPER);
+			}
 			line6_stream_stop(line6pcm, s->stream,
 					  LINE6_STREAM_PCM);
 			break;
@@ -283,27 +297,30 @@ snd_pcm_uframes_t snd_line6_pointer(struct snd_pcm_substream *substream)
 	return pstr->pos_done;
 }
 
-/* Acquire and start duplex streams:
+/* 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)
+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, type);
+		ret = line6_buffer_acquire(line6pcm, pstr, dir, 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;
+	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);
@@ -339,7 +356,7 @@ int snd_line6_hw_params(struct snd_pcm_substream *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);
+	ret = line6_buffer_acquire(line6pcm, pstr, substream->stream, LINE6_STREAM_PCM);
 	if (ret < 0)
 		goto error;
 
diff --git a/sound/usb/line6/pcm.h b/sound/usb/line6/pcm.h
index 58d36f9..5f796ef8 100644
--- a/sound/usb/line6/pcm.h
+++ b/sound/usb/line6/pcm.h
@@ -72,6 +72,7 @@ enum {
 	LINE6_STREAM_PCM,
 	LINE6_STREAM_MONITOR,
 	LINE6_STREAM_IMPULSE,
+	LINE6_STREAM_CAPTURE_HELPER,
 };
 
 /* misc bit flags for PCM operation */
@@ -190,7 +191,8 @@ extern int snd_line6_hw_params(struct snd_pcm_substream *substream,
 extern int snd_line6_hw_free(struct snd_pcm_substream *substream);
 extern snd_pcm_uframes_t snd_line6_pointer(struct snd_pcm_substream *substream);
 extern void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm);
-extern int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type);
+extern int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type,
+			       bool start);
 extern void line6_pcm_release(struct snd_line6_pcm *line6pcm, int type);
 
 #endif
diff --git a/sound/usb/line6/toneport.c b/sound/usb/line6/toneport.c
index da76e03..8e22f43 100644
--- a/sound/usb/line6/toneport.c
+++ b/sound/usb/line6/toneport.c
@@ -177,7 +177,7 @@ static int snd_toneport_monitor_put(struct snd_kcontrol *kcontrol,
 	line6pcm->volume_monitor = ucontrol->value.integer.value[0];
 
 	if (line6pcm->volume_monitor > 0) {
-		err = line6_pcm_acquire(line6pcm, LINE6_STREAM_MONITOR);
+		err = line6_pcm_acquire(line6pcm, LINE6_STREAM_MONITOR, true);
 		if (err < 0) {
 			line6pcm->volume_monitor = 0;
 			line6_pcm_release(line6pcm, LINE6_STREAM_MONITOR);
@@ -246,7 +246,7 @@ static void toneport_start_pcm(unsigned long arg)
 	struct usb_line6_toneport *toneport = (struct usb_line6_toneport *)arg;
 	struct usb_line6 *line6 = &toneport->line6;
 
-	line6_pcm_acquire(line6->line6pcm, LINE6_STREAM_MONITOR);
+	line6_pcm_acquire(line6->line6pcm, LINE6_STREAM_MONITOR, true);
 }
 
 /* control definition */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v3 06/12] ALSA: line6: Distinguish device init (ctrl EP) and MIDI data transfer (int EP)
  2016-09-16  9:12 ` [PATCH v3 00/12] Line6 POD X3/X3Live suport Andrej Krutak
                     ` (4 preceding siblings ...)
  2016-09-16  9:12   ` [PATCH v3 05/12] ALSA: line6: Add LINE6_CAP_IN_NEEDS_OUT, a void playback stream during capture Andrej Krutak
@ 2016-09-16  9:12   ` Andrej Krutak
  2016-09-16  9:12   ` [PATCH v3 07/12] ALSA: line6: Allow processing of raw incoming messages Andrej Krutak
                     ` (5 subsequent siblings)
  11 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-09-16  9:12 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

POD X3 can initialize similarly to older PODs, but it doesn't have the MIDI
interface. Instead, configuration is done via proprietary bulk EP messages.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/driver.c | 72 ++++++++++++++++++++++++++++++++++--------------
 sound/usb/line6/driver.h |  6 ++--
 sound/usb/line6/pod.c    |  9 +++++-
 sound/usb/line6/variax.c |  6 ++--
 4 files changed, 68 insertions(+), 25 deletions(-)

diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 14032d9..9b16777 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -66,10 +66,17 @@ static int line6_start_listen(struct usb_line6 *line6)
 {
 	int err;
 
-	usb_fill_int_urb(line6->urb_listen, line6->usbdev,
-		usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r),
-		line6->buffer_listen, LINE6_BUFSIZE_LISTEN,
-		line6_data_received, line6, line6->interval);
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+		usb_fill_int_urb(line6->urb_listen, line6->usbdev,
+			usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r),
+			line6->buffer_listen, LINE6_BUFSIZE_LISTEN,
+			line6_data_received, line6, line6->interval);
+	} else {
+		usb_fill_bulk_urb(line6->urb_listen, line6->usbdev,
+			usb_rcvbulkpipe(line6->usbdev, line6->properties->ep_ctrl_r),
+			line6->buffer_listen, LINE6_BUFSIZE_LISTEN,
+			line6_data_received, line6);
+	}
 	line6->urb_listen->actual_length = 0;
 	err = usb_submit_urb(line6->urb_listen, GFP_ATOMIC);
 	return err;
@@ -90,6 +97,7 @@ static int line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
 				  int size)
 {
 	int i, done = 0;
+	const struct line6_properties *properties = line6->properties;
 
 	for (i = 0; i < size; i += line6->max_packet_size) {
 		int partial;
@@ -97,15 +105,21 @@ static int line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
 		int frag_size = min(line6->max_packet_size, size - i);
 		int retval;
 
-		retval = usb_interrupt_msg(line6->usbdev,
-					usb_sndintpipe(line6->usbdev,
-						line6->properties->ep_ctrl_w),
-					(char *)frag_buf, frag_size,
-					&partial, LINE6_TIMEOUT * HZ);
+		if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+			retval = usb_interrupt_msg(line6->usbdev,
+						usb_sndintpipe(line6->usbdev, properties->ep_ctrl_w),
+						(char *)frag_buf, frag_size,
+						&partial, LINE6_TIMEOUT * HZ);
+		} else {
+			retval = usb_bulk_msg(line6->usbdev,
+						usb_sndbulkpipe(line6->usbdev, properties->ep_ctrl_w),
+						(char *)frag_buf, frag_size,
+						&partial, LINE6_TIMEOUT * HZ);
+		}
 
 		if (retval) {
 			dev_err(line6->ifcdev,
-				"usb_interrupt_msg failed (%d)\n", retval);
+				"usb_bulk_msg failed (%d)\n", retval);
 			break;
 		}
 
@@ -140,10 +154,17 @@ static int line6_send_raw_message_async_part(struct message *msg,
 	int done = msg->done;
 	int bytes = min(msg->size - done, line6->max_packet_size);
 
-	usb_fill_int_urb(urb, line6->usbdev,
-		usb_sndintpipe(line6->usbdev, line6->properties->ep_ctrl_w),
-		(char *)msg->buffer + done, bytes,
-		line6_async_request_sent, msg, line6->interval);
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+		usb_fill_int_urb(urb, line6->usbdev,
+			usb_sndintpipe(line6->usbdev, line6->properties->ep_ctrl_w),
+			(char *)msg->buffer + done, bytes,
+			line6_async_request_sent, msg, line6->interval);
+	} else {
+		usb_fill_bulk_urb(urb, line6->usbdev,
+			usb_sndbulkpipe(line6->usbdev, line6->properties->ep_ctrl_w),
+			(char *)msg->buffer + done, bytes,
+			line6_async_request_sent, msg);
+	}
 
 	msg->done += bytes;
 	retval = usb_submit_urb(urb, GFP_ATOMIC);
@@ -462,7 +483,18 @@ static void line6_destruct(struct snd_card *card)
 static void line6_get_interval(struct usb_line6 *line6)
 {
 	struct usb_device *usbdev = line6->usbdev;
-	struct usb_host_endpoint *ep = usbdev->ep_in[line6->properties->ep_ctrl_r];
+	const struct line6_properties *properties = line6->properties;
+	int pipe;
+	struct usb_host_endpoint *ep;
+
+	if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+		pipe =
+			usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r);
+	} else {
+		pipe =
+			usb_rcvbulkpipe(line6->usbdev, line6->properties->ep_ctrl_r);
+	}
+	ep = usbdev->ep_in[usb_pipeendpoint(pipe)];
 
 	if (ep) {
 		line6->interval = ep->desc.bInterval;
@@ -483,7 +515,7 @@ static void line6_get_interval(struct usb_line6 *line6)
 	}
 }
 
-static int line6_init_cap_control(struct usb_line6 *line6)
+static int line6_init_cap_control_midi(struct usb_line6 *line6)
 {
 	int ret;
 
@@ -573,8 +605,8 @@ int line6_probe(struct usb_interface *interface,
 
 	line6_get_interval(line6);
 
-	if (properties->capabilities & LINE6_CAP_CONTROL) {
-		ret = line6_init_cap_control(line6);
+	if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+		ret = line6_init_cap_control_midi(line6);
 		if (ret < 0)
 			goto error;
 	}
@@ -644,7 +676,7 @@ int line6_suspend(struct usb_interface *interface, pm_message_t message)
 
 	snd_power_change_state(line6->card, SNDRV_CTL_POWER_D3hot);
 
-	if (line6->properties->capabilities & LINE6_CAP_CONTROL)
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)
 		line6_stop_listen(line6);
 
 	if (line6pcm != NULL) {
@@ -663,7 +695,7 @@ int line6_resume(struct usb_interface *interface)
 {
 	struct usb_line6 *line6 = usb_get_intfdata(interface);
 
-	if (line6->properties->capabilities & LINE6_CAP_CONTROL)
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)
 		line6_start_listen(line6);
 
 	snd_power_change_state(line6->card, SNDRV_CTL_POWER_D0);
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index 0bcab38..d48c7d2 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -104,6 +104,8 @@ enum {
 	LINE6_CAP_HWMON =	1 << 2,
 	/* device requires output data when input is read */
 	LINE6_CAP_IN_NEEDS_OUT = 1 << 3,
+	/* device uses raw MIDI via USB (data endpoints) */
+	LINE6_CAP_CONTROL_MIDI = 1 << 4,
 };
 
 /*
@@ -142,10 +144,10 @@ struct usb_line6 {
 	/* Line 6 MIDI device data structure */
 	struct snd_line6_midi *line6midi;
 
-	/* URB for listening to PODxt Pro control endpoint */
+	/* URB for listening to POD data endpoint */
 	struct urb *urb_listen;
 
-	/* Buffer for listening to PODxt Pro control endpoint */
+	/* Buffer for listening to POD data endpoint */
 	unsigned char *buffer_listen;
 
 	/* Buffer for message to be processed */
diff --git a/sound/usb/line6/pod.c b/sound/usb/line6/pod.c
index 36e7274..17aa616 100644
--- a/sound/usb/line6/pod.c
+++ b/sound/usb/line6/pod.c
@@ -475,6 +475,7 @@ static const struct line6_properties pod_properties_table[] = {
 		.id = "BassPODxt",
 		.name = "BassPODxt",
 		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI
 				| LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 5,
@@ -487,6 +488,7 @@ static const struct line6_properties pod_properties_table[] = {
 		.id = "BassPODxtLive",
 		.name = "BassPODxt Live",
 		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI
 				| LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 1,
@@ -499,6 +501,7 @@ static const struct line6_properties pod_properties_table[] = {
 		.id = "BassPODxtPro",
 		.name = "BassPODxt Pro",
 		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI
 				| LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 5,
@@ -510,7 +513,8 @@ static const struct line6_properties pod_properties_table[] = {
 	[LINE6_POCKETPOD] = {
 		.id = "PocketPOD",
 		.name = "Pocket POD",
-		.capabilities	= LINE6_CAP_CONTROL,
+		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI,
 		.altsetting = 0,
 		.ep_ctrl_r = 0x82,
 		.ep_ctrl_w = 0x02,
@@ -520,6 +524,7 @@ static const struct line6_properties pod_properties_table[] = {
 		.id = "PODxt",
 		.name = "PODxt",
 		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI
 				| LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 5,
@@ -532,6 +537,7 @@ static const struct line6_properties pod_properties_table[] = {
 		.id = "PODxtLive",
 		.name = "PODxt Live",
 		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI
 				| LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 1,
@@ -544,6 +550,7 @@ static const struct line6_properties pod_properties_table[] = {
 		.id = "PODxtPro",
 		.name = "PODxt Pro",
 		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI
 				| LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 5,
diff --git a/sound/usb/line6/variax.c b/sound/usb/line6/variax.c
index ddc23dd..0c4512d 100644
--- a/sound/usb/line6/variax.c
+++ b/sound/usb/line6/variax.c
@@ -259,7 +259,8 @@ static const struct line6_properties variax_properties_table[] = {
 	[LINE6_PODXTLIVE_VARIAX] = {
 		.id = "PODxtLive",
 		.name = "PODxt Live",
-		.capabilities	= LINE6_CAP_CONTROL,
+		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI,
 		.altsetting = 1,
 		.ep_ctrl_r = 0x86,
 		.ep_ctrl_w = 0x05,
@@ -269,7 +270,8 @@ static const struct line6_properties variax_properties_table[] = {
 	[LINE6_VARIAX] = {
 		.id = "Variax",
 		.name = "Variax Workbench",
-		.capabilities	= LINE6_CAP_CONTROL,
+		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI,
 		.altsetting = 1,
 		.ep_ctrl_r = 0x82,
 		.ep_ctrl_w = 0x01,
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v3 07/12] ALSA: line6: Allow processing of raw incoming messages
  2016-09-16  9:12 ` [PATCH v3 00/12] Line6 POD X3/X3Live suport Andrej Krutak
                     ` (5 preceding siblings ...)
  2016-09-16  9:12   ` [PATCH v3 06/12] ALSA: line6: Distinguish device init (ctrl EP) and MIDI data transfer (int EP) Andrej Krutak
@ 2016-09-16  9:12   ` Andrej Krutak
  2016-09-16  9:12   ` [PATCH v3 08/12] ALSA: line6: Add support for POD X3 Andrej Krutak
                     ` (4 subsequent siblings)
  11 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-09-16  9:12 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

Not all PODs use MIDI via USB data interface, thus allow avoiding
that code and instead using direct processing.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/driver.c | 59 ++++++++++++++++++++++++++++--------------------
 sound/usb/line6/driver.h |  8 ++++---
 sound/usb/line6/midi.c   |  2 +-
 3 files changed, 40 insertions(+), 29 deletions(-)

diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 9b16777..853a143 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -290,26 +290,31 @@ static void line6_data_received(struct urb *urb)
 	if (urb->status == -ESHUTDOWN)
 		return;
 
-	done =
-	    line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length);
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+		done =
+			line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length);
 
-	if (done < urb->actual_length) {
-		line6_midibuf_ignore(mb, done);
-		dev_dbg(line6->ifcdev, "%d %d buffer overflow - message skipped\n",
-			done, urb->actual_length);
-	}
+		if (done < urb->actual_length) {
+			line6_midibuf_ignore(mb, done);
+			dev_dbg(line6->ifcdev, "%d %d buffer overflow - message skipped\n",
+				done, urb->actual_length);
+		}
 
-	for (;;) {
-		done =
-		    line6_midibuf_read(mb, line6->buffer_message,
-				       LINE6_MESSAGE_MAXLEN);
+		for (;;) {
+			done =
+				line6_midibuf_read(mb, line6->buffer_message,
+						LINE6_MESSAGE_MAXLEN);
 
-		if (done == 0)
-			break;
+			if (done == 0)
+				break;
 
-		line6->message_length = done;
-		line6_midi_receive(line6, line6->buffer_message, done);
+			line6->message_length = done;
+			line6_midi_receive(line6, line6->buffer_message, done);
 
+			if (line6->process_message)
+				line6->process_message(line6);
+		}
+	} else {
 		if (line6->process_message)
 			line6->process_message(line6);
 	}
@@ -469,7 +474,9 @@ static void line6_destruct(struct snd_card *card)
 	struct usb_device *usbdev = line6->usbdev;
 
 	/* free buffer memory first: */
-	kfree(line6->buffer_message);
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)
+		kfree(line6->buffer_message);
+
 	kfree(line6->buffer_listen);
 
 	/* then free URBs: */
@@ -515,7 +522,7 @@ static void line6_get_interval(struct usb_line6 *line6)
 	}
 }
 
-static int line6_init_cap_control_midi(struct usb_line6 *line6)
+static int line6_init_cap_control(struct usb_line6 *line6)
 {
 	int ret;
 
@@ -524,14 +531,16 @@ static int line6_init_cap_control_midi(struct usb_line6 *line6)
 	if (!line6->buffer_listen)
 		return -ENOMEM;
 
-	line6->buffer_message = kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL);
-	if (!line6->buffer_message)
-		return -ENOMEM;
-
 	line6->urb_listen = usb_alloc_urb(0, GFP_KERNEL);
 	if (!line6->urb_listen)
 		return -ENOMEM;
 
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+		line6->buffer_message = kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL);
+		if (!line6->buffer_message)
+			return -ENOMEM;
+	}
+
 	ret = line6_start_listen(line6);
 	if (ret < 0) {
 		dev_err(line6->ifcdev, "cannot start listening: %d\n", ret);
@@ -605,8 +614,8 @@ int line6_probe(struct usb_interface *interface,
 
 	line6_get_interval(line6);
 
-	if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
-		ret = line6_init_cap_control_midi(line6);
+	if (properties->capabilities & LINE6_CAP_CONTROL) {
+		ret = line6_init_cap_control(line6);
 		if (ret < 0)
 			goto error;
 	}
@@ -676,7 +685,7 @@ int line6_suspend(struct usb_interface *interface, pm_message_t message)
 
 	snd_power_change_state(line6->card, SNDRV_CTL_POWER_D3hot);
 
-	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL)
 		line6_stop_listen(line6);
 
 	if (line6pcm != NULL) {
@@ -695,7 +704,7 @@ int line6_resume(struct usb_interface *interface)
 {
 	struct usb_line6 *line6 = usb_get_intfdata(interface);
 
-	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL)
 		line6_start_listen(line6);
 
 	snd_power_change_state(line6->card, SNDRV_CTL_POWER_D0);
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index d48c7d2..d7eefe1 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -147,15 +147,17 @@ struct usb_line6 {
 	/* URB for listening to POD data endpoint */
 	struct urb *urb_listen;
 
-	/* Buffer for listening to POD data endpoint */
+	/* Buffer for incoming data from POD data endpoint */
 	unsigned char *buffer_listen;
 
-	/* Buffer for message to be processed */
+	/* Buffer for message to be processed, generated from MIDI layer */
 	unsigned char *buffer_message;
 
-	/* Length of message to be processed */
+	/* Length of message to be processed, generated from MIDI layer  */
 	int message_length;
 
+	/* If MIDI is supported, buffer_message contains the pre-processed data;
+	 * otherwise the data is only in urb_listen (buffer_incoming). */
 	void (*process_message)(struct usb_line6 *);
 	void (*disconnect)(struct usb_line6 *line6);
 };
diff --git a/sound/usb/line6/midi.c b/sound/usb/line6/midi.c
index cebea9b..d0fb2f2 100644
--- a/sound/usb/line6/midi.c
+++ b/sound/usb/line6/midi.c
@@ -258,7 +258,7 @@ int line6_init_midi(struct usb_line6 *line6)
 	struct snd_rawmidi *rmidi;
 	struct snd_line6_midi *line6midi;
 
-	if (!(line6->properties->capabilities & LINE6_CAP_CONTROL)) {
+	if (!(line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)) {
 		/* skip MIDI initialization and report success */
 		return 0;
 	}
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v3 08/12] ALSA: line6: Add support for POD X3
  2016-09-16  9:12 ` [PATCH v3 00/12] Line6 POD X3/X3Live suport Andrej Krutak
                     ` (6 preceding siblings ...)
  2016-09-16  9:12   ` [PATCH v3 07/12] ALSA: line6: Allow processing of raw incoming messages Andrej Krutak
@ 2016-09-16  9:12   ` Andrej Krutak
  2016-09-16  9:12   ` [PATCH v3 09/12] ALSA: line6: Add support for POD X3 Live (only USB ID differs from POD X3) Andrej Krutak
                     ` (3 subsequent siblings)
  11 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-09-16  9:12 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

This includes audio in/out and basic initialization via control EP (emulates
what original driver does). The initialization is done similarly to original
POD, firmware and serial IDs are read and exported via sysfs.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/Kconfig |   4 +-
 sound/usb/line6/podhd.c | 276 +++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 265 insertions(+), 15 deletions(-)

diff --git a/sound/usb/line6/Kconfig b/sound/usb/line6/Kconfig
index f4585d37..8ffcf48 100644
--- a/sound/usb/line6/Kconfig
+++ b/sound/usb/line6/Kconfig
@@ -21,10 +21,10 @@ config SND_USB_POD
 	      re-amping)
 
 config SND_USB_PODHD
-	tristate "Line 6 POD HD300/400/500 USB support"
+	tristate "Line 6 POD X3/HD300/400/500 USB support"
 	select SND_USB_LINE6
 	help
-	  This is a driver for POD HD300, 400 and 500 devices.
+	  This is a driver for POD X3, HD300, 400 and 500 devices.
 
 config SND_USB_TONEPORT
 	tristate "TonePort GX, UX1 and UX2 USB support"
diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c
index 4fc4789..8cf0a98 100644
--- a/sound/usb/line6/podhd.c
+++ b/sound/usb/line6/podhd.c
@@ -2,6 +2,7 @@
  * Line 6 Pod HD
  *
  * Copyright (C) 2011 Stefan Hajnoczi <stefanha@gmail.com>
+ * Copyright (C) 2015 Andrej Krutak <dev@andree.sk>
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License as
@@ -18,11 +19,44 @@
 #include "driver.h"
 #include "pcm.h"
 
+#define PODHD_STARTUP_DELAY 500
+
+/*
+ * Stages of POD startup procedure
+ */
+enum {
+	PODHD_STARTUP_INIT = 1,
+	PODHD_STARTUP_SCHEDULE_WORKQUEUE,
+	PODHD_STARTUP_SETUP,
+	PODHD_STARTUP_LAST = PODHD_STARTUP_SETUP - 1
+};
+
 enum {
 	LINE6_PODHD300,
 	LINE6_PODHD400,
 	LINE6_PODHD500_0,
 	LINE6_PODHD500_1,
+	LINE6_PODX3,
+};
+
+struct usb_line6_podhd {
+	/* Generic Line 6 USB data */
+	struct usb_line6 line6;
+
+	/* Timer for device initialization */
+	struct timer_list startup_timer;
+
+	/* Work handler for device initialization */
+	struct work_struct startup_work;
+
+	/* Current progress in startup procedure */
+	int startup_progress;
+
+	/* Serial number of device */
+	u32 serial_number;
+
+	/* Firmware version */
+	int firmware_version;
 };
 
 static struct snd_ratden podhd_ratden = {
@@ -71,9 +105,196 @@ static struct line6_pcm_properties podhd_pcm_properties = {
 	.rates = {
 			    .nrats = 1,
 			    .rats = &podhd_ratden},
-	.bytes_per_channel = 3 /* 24bit audio (stereo) */
+	.bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */
 };
 
+static struct line6_pcm_properties podx3_pcm_properties = {
+	.playback_hw = {
+				  .info = (SNDRV_PCM_INFO_MMAP |
+					   SNDRV_PCM_INFO_INTERLEAVED |
+					   SNDRV_PCM_INFO_BLOCK_TRANSFER |
+					   SNDRV_PCM_INFO_MMAP_VALID |
+					   SNDRV_PCM_INFO_PAUSE |
+					   SNDRV_PCM_INFO_SYNC_START),
+				  .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+				  .rates = SNDRV_PCM_RATE_48000,
+				  .rate_min = 48000,
+				  .rate_max = 48000,
+				  .channels_min = 2,
+				  .channels_max = 2,
+				  .buffer_bytes_max = 60000,
+				  .period_bytes_min = 64,
+				  .period_bytes_max = 8192,
+				  .periods_min = 1,
+				  .periods_max = 1024},
+	.capture_hw = {
+				 .info = (SNDRV_PCM_INFO_MMAP |
+					  SNDRV_PCM_INFO_INTERLEAVED |
+					  SNDRV_PCM_INFO_BLOCK_TRANSFER |
+					  SNDRV_PCM_INFO_MMAP_VALID |
+					  SNDRV_PCM_INFO_SYNC_START),
+				 .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+				 .rates = SNDRV_PCM_RATE_48000,
+				 .rate_min = 48000,
+				 .rate_max = 48000,
+				 /* 1+2: Main signal (out), 3+4: Tone 1,
+				  * 5+6: Tone 2, 7+8: raw */
+				 .channels_min = 8,
+				 .channels_max = 8,
+				 .buffer_bytes_max = 60000,
+				 .period_bytes_min = 64,
+				 .period_bytes_max = 8192,
+				 .periods_min = 1,
+				 .periods_max = 1024},
+	.rates = {
+			    .nrats = 1,
+			    .rats = &podhd_ratden},
+	.bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */
+};
+
+static void podhd_startup_start_workqueue(unsigned long data);
+static void podhd_startup_workqueue(struct work_struct *work);
+static int podhd_startup_finalize(struct usb_line6_podhd *pod);
+
+static ssize_t serial_number_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct snd_card *card = dev_to_snd_card(dev);
+	struct usb_line6_podhd *pod = card->private_data;
+
+	return sprintf(buf, "%u\n", pod->serial_number);
+}
+
+static ssize_t firmware_version_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct snd_card *card = dev_to_snd_card(dev);
+	struct usb_line6_podhd *pod = card->private_data;
+
+	return sprintf(buf, "%06x\n", pod->firmware_version);
+}
+
+static DEVICE_ATTR_RO(firmware_version);
+static DEVICE_ATTR_RO(serial_number);
+
+static struct attribute *podhd_dev_attrs[] = {
+	&dev_attr_firmware_version.attr,
+	&dev_attr_serial_number.attr,
+	NULL
+};
+
+static const struct attribute_group podhd_dev_attr_group = {
+	.name = "podhd",
+	.attrs = podhd_dev_attrs,
+};
+
+/*
+ * POD X3 startup procedure.
+ *
+ * May be compatible with other POD HD's, since it's also similar to the
+ * previous POD setup. In any case, it doesn't seem to be required for the
+ * audio nor bulk interfaces to work.
+ */
+
+static void podhd_startup(struct usb_line6_podhd *pod)
+{
+	CHECK_STARTUP_PROGRESS(pod->startup_progress, PODHD_STARTUP_INIT);
+
+	/* delay startup procedure: */
+	line6_start_timer(&pod->startup_timer, PODHD_STARTUP_DELAY,
+		podhd_startup_start_workqueue, (unsigned long)pod);
+}
+
+static void podhd_startup_start_workqueue(unsigned long data)
+{
+	struct usb_line6_podhd *pod = (struct usb_line6_podhd *)data;
+
+	CHECK_STARTUP_PROGRESS(pod->startup_progress,
+		PODHD_STARTUP_SCHEDULE_WORKQUEUE);
+
+	/* schedule work for global work queue: */
+	schedule_work(&pod->startup_work);
+}
+
+static int podhd_dev_start(struct usb_line6_podhd *pod)
+{
+	int ret;
+	u8 init_bytes[8];
+	int i;
+	struct usb_device *usbdev = pod->line6.usbdev;
+
+	ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
+					0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+					0x11, 0,
+					NULL, 0, LINE6_TIMEOUT * HZ);
+	if (ret < 0) {
+		dev_err(pod->line6.ifcdev, "read request failed (error %d)\n", ret);
+		return ret;
+	}
+
+	/* NOTE: looks like some kind of ping message */
+	ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67,
+					USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+					0x11, 0x0,
+					&init_bytes, 3, LINE6_TIMEOUT * HZ);
+	if (ret < 0) {
+		dev_err(pod->line6.ifcdev,
+			"receive length failed (error %d)\n", ret);
+		return ret;
+	}
+
+	pod->firmware_version =
+		(init_bytes[0] << 16) | (init_bytes[1] << 8) | (init_bytes[2] << 0);
+
+	for (i = 0; i <= 16; i++) {
+		ret = line6_read_data(&pod->line6, 0xf000 + 0x08 * i, init_bytes, 8);
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
+					USB_REQ_SET_FEATURE,
+					USB_TYPE_STANDARD | USB_RECIP_DEVICE | USB_DIR_OUT,
+					1, 0,
+					NULL, 0, LINE6_TIMEOUT * HZ);
+	if (ret < 0) {
+		return ret;
+	}
+
+	return 0;
+}
+
+static void podhd_startup_workqueue(struct work_struct *work)
+{
+	struct usb_line6_podhd *pod =
+	    container_of(work, struct usb_line6_podhd, startup_work);
+
+	CHECK_STARTUP_PROGRESS(pod->startup_progress, PODHD_STARTUP_SETUP);
+
+	podhd_dev_start(pod);
+	line6_read_serial_number(&pod->line6, &pod->serial_number);
+
+	podhd_startup_finalize(pod);
+}
+
+static int podhd_startup_finalize(struct usb_line6_podhd *pod)
+{
+	struct usb_line6 *line6 = &pod->line6;
+
+	/* ALSA audio interface: */
+	return snd_card_register(line6->card);
+}
+
+static void podhd_disconnect(struct usb_line6 *line6)
+{
+	struct usb_line6_podhd *pod = (struct usb_line6_podhd *)line6;
+
+	if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) {
+		del_timer_sync(&pod->startup_timer);
+		cancel_work_sync(&pod->startup_work);
+	}
+}
+
 /*
 	Try to init POD HD device.
 */
@@ -81,6 +302,16 @@ static int podhd_init(struct usb_line6 *line6,
 		      const struct usb_device_id *id)
 {
 	int err;
+	struct usb_line6_podhd *pod = (struct usb_line6_podhd *) line6;
+
+	line6->disconnect = podhd_disconnect;
+
+	if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) {
+		/* create sysfs entries: */
+		err = snd_card_add_dev_attr(line6->card, &podhd_dev_attr_group);
+		if (err < 0)
+			return err;
+	}
 
 	/* initialize MIDI subsystem: */
 	err = line6_init_midi(line6);
@@ -88,12 +319,22 @@ static int podhd_init(struct usb_line6 *line6,
 		return err;
 
 	/* initialize PCM subsystem: */
-	err = line6_init_pcm(line6, &podhd_pcm_properties);
+	err = line6_init_pcm(line6,
+		(id->driver_info == LINE6_PODX3) ? &podx3_pcm_properties :
+		&podhd_pcm_properties);
 	if (err < 0)
 		return err;
 
-	/* register USB audio system: */
-	return snd_card_register(line6->card);
+	if (!(pod->line6.properties->capabilities & LINE6_CAP_CONTROL)) {
+		/* register USB audio system directly */
+		return podhd_startup_finalize(pod);
+	}
+
+	/* init device and delay registering */
+	init_timer(&pod->startup_timer);
+	INIT_WORK(&pod->startup_work, podhd_startup_workqueue);
+	podhd_startup(pod);
+	return 0;
 }
 
 #define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod)
@@ -101,10 +342,12 @@ static int podhd_init(struct usb_line6 *line6,
 
 /* table of devices that work with this driver */
 static const struct usb_device_id podhd_id_table[] = {
+	/* TODO: no need to alloc data interfaces when only audio is used */
 	{ LINE6_DEVICE(0x5057),    .driver_info = LINE6_PODHD300 },
 	{ LINE6_DEVICE(0x5058),    .driver_info = LINE6_PODHD400 },
 	{ LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500_0 },
 	{ LINE6_IF_NUM(0x414D, 1), .driver_info = LINE6_PODHD500_1 },
+	{ LINE6_IF_NUM(0x414A, 0), .driver_info = LINE6_PODX3 },
 	{}
 };
 
@@ -114,8 +357,7 @@ static const struct line6_properties podhd_properties_table[] = {
 	[LINE6_PODHD300] = {
 		.id = "PODHD300",
 		.name = "POD HD300",
-		.capabilities	= LINE6_CAP_CONTROL
-				| LINE6_CAP_PCM
+		.capabilities	= LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 5,
 		.ep_ctrl_r = 0x84,
@@ -126,8 +368,7 @@ static const struct line6_properties podhd_properties_table[] = {
 	[LINE6_PODHD400] = {
 		.id = "PODHD400",
 		.name = "POD HD400",
-		.capabilities	= LINE6_CAP_CONTROL
-				| LINE6_CAP_PCM
+		.capabilities	= LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 5,
 		.ep_ctrl_r = 0x84,
@@ -138,8 +379,7 @@ static const struct line6_properties podhd_properties_table[] = {
 	[LINE6_PODHD500_0] = {
 		.id = "PODHD500",
 		.name = "POD HD500",
-		.capabilities	= LINE6_CAP_CONTROL
-				| LINE6_CAP_PCM
+		.capabilities	= LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 1,
 		.ep_ctrl_r = 0x81,
@@ -150,8 +390,7 @@ static const struct line6_properties podhd_properties_table[] = {
 	[LINE6_PODHD500_1] = {
 		.id = "PODHD500",
 		.name = "POD HD500",
-		.capabilities	= LINE6_CAP_CONTROL
-				| LINE6_CAP_PCM
+		.capabilities	= LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 1,
 		.ep_ctrl_r = 0x81,
@@ -159,6 +398,17 @@ static const struct line6_properties podhd_properties_table[] = {
 		.ep_audio_r = 0x86,
 		.ep_audio_w = 0x02,
 	},
+	[LINE6_PODX3] = {
+		.id = "PODX3",
+		.name = "POD X3",
+		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_PCM | LINE6_CAP_HWMON | LINE6_CAP_IN_NEEDS_OUT,
+		.altsetting = 1,
+		.ep_ctrl_r = 0x81,
+		.ep_ctrl_w = 0x01,
+		.ep_audio_r = 0x86,
+		.ep_audio_w = 0x02,
+	},
 };
 
 /*
@@ -169,7 +419,7 @@ static int podhd_probe(struct usb_interface *interface,
 {
 	return line6_probe(interface, id, "Line6-PODHD",
 			   &podhd_properties_table[id->driver_info],
-			   podhd_init, sizeof(struct usb_line6));
+			   podhd_init, sizeof(struct usb_line6_podhd));
 }
 
 static struct usb_driver podhd_driver = {
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v3 09/12] ALSA: line6: Add support for POD X3 Live (only USB ID differs from POD X3)
  2016-09-16  9:12 ` [PATCH v3 00/12] Line6 POD X3/X3Live suport Andrej Krutak
                     ` (7 preceding siblings ...)
  2016-09-16  9:12   ` [PATCH v3 08/12] ALSA: line6: Add support for POD X3 Andrej Krutak
@ 2016-09-16  9:12   ` Andrej Krutak
  2016-09-16  9:12   ` [PATCH v3 10/12] ALSA: line6: Only determine control port properties if needed Andrej Krutak
                     ` (2 subsequent siblings)
  11 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-09-16  9:12 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/podhd.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c
index 8cf0a98..8246ea5 100644
--- a/sound/usb/line6/podhd.c
+++ b/sound/usb/line6/podhd.c
@@ -37,6 +37,7 @@ enum {
 	LINE6_PODHD500_0,
 	LINE6_PODHD500_1,
 	LINE6_PODX3,
+	LINE6_PODX3LIVE
 };
 
 struct usb_line6_podhd {
@@ -348,6 +349,7 @@ static const struct usb_device_id podhd_id_table[] = {
 	{ LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500_0 },
 	{ LINE6_IF_NUM(0x414D, 1), .driver_info = LINE6_PODHD500_1 },
 	{ LINE6_IF_NUM(0x414A, 0), .driver_info = LINE6_PODX3 },
+	{ LINE6_IF_NUM(0x414B, 0), .driver_info = LINE6_PODX3LIVE },
 	{}
 };
 
@@ -409,6 +411,17 @@ static const struct line6_properties podhd_properties_table[] = {
 		.ep_audio_r = 0x86,
 		.ep_audio_w = 0x02,
 	},
+	[LINE6_PODX3LIVE] = {
+		.id = "PODX3LIVE",
+		.name = "POD X3 LIVE",
+		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_PCM | LINE6_CAP_HWMON | LINE6_CAP_IN_NEEDS_OUT,
+		.altsetting = 1,
+		.ep_ctrl_r = 0x81,
+		.ep_ctrl_w = 0x01,
+		.ep_audio_r = 0x86,
+		.ep_audio_w = 0x02,
+	},
 };
 
 /*
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v3 10/12] ALSA: line6: Only determine control port properties if needed
  2016-09-16  9:12 ` [PATCH v3 00/12] Line6 POD X3/X3Live suport Andrej Krutak
                     ` (8 preceding siblings ...)
  2016-09-16  9:12   ` [PATCH v3 09/12] ALSA: line6: Add support for POD X3 Live (only USB ID differs from POD X3) Andrej Krutak
@ 2016-09-16  9:12   ` Andrej Krutak
  2016-09-16  9:12   ` [PATCH v3 11/12] ALSA: line6: Cleanup podhd initialization Andrej Krutak
  2016-09-16  9:12   ` [PATCH v3 12/12] ALSA: line6: Add hwdep interface to access the POD control messages Andrej Krutak
  11 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-09-16  9:12 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

Not all line6 devices use the control port.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/driver.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 853a143..8a71d45 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -612,9 +612,8 @@ int line6_probe(struct usb_interface *interface,
 		goto error;
 	}
 
-	line6_get_interval(line6);
-
 	if (properties->capabilities & LINE6_CAP_CONTROL) {
+		line6_get_interval(line6);
 		ret = line6_init_cap_control(line6);
 		if (ret < 0)
 			goto error;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v3 11/12] ALSA: line6: Cleanup podhd initialization
  2016-09-16  9:12 ` [PATCH v3 00/12] Line6 POD X3/X3Live suport Andrej Krutak
                     ` (9 preceding siblings ...)
  2016-09-16  9:12   ` [PATCH v3 10/12] ALSA: line6: Only determine control port properties if needed Andrej Krutak
@ 2016-09-16  9:12   ` Andrej Krutak
  2016-09-16  9:12   ` [PATCH v3 12/12] ALSA: line6: Add hwdep interface to access the POD control messages Andrej Krutak
  11 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-09-16  9:12 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

Only initialize PCM for POD HD devices that support it.
No POD HD seems to support MIDI, thus drop the initialization.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/podhd.c | 19 ++++++++-----------
 1 file changed, 8 insertions(+), 11 deletions(-)

diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c
index 8246ea5..193eb29 100644
--- a/sound/usb/line6/podhd.c
+++ b/sound/usb/line6/podhd.c
@@ -314,17 +314,14 @@ static int podhd_init(struct usb_line6 *line6,
 			return err;
 	}
 
-	/* initialize MIDI subsystem: */
-	err = line6_init_midi(line6);
-	if (err < 0)
-		return err;
-
-	/* initialize PCM subsystem: */
-	err = line6_init_pcm(line6,
-		(id->driver_info == LINE6_PODX3) ? &podx3_pcm_properties :
-		&podhd_pcm_properties);
-	if (err < 0)
-		return err;
+	if (pod->line6.properties->capabilities & LINE6_CAP_PCM) {
+		/* initialize PCM subsystem: */
+		err = line6_init_pcm(line6,
+			(id->driver_info == LINE6_PODX3) ? &podx3_pcm_properties :
+			&podhd_pcm_properties);
+		if (err < 0)
+			return err;
+	}
 
 	if (!(pod->line6.properties->capabilities & LINE6_CAP_CONTROL)) {
 		/* register USB audio system directly */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v3 12/12] ALSA: line6: Add hwdep interface to access the POD control messages
  2016-09-16  9:12 ` [PATCH v3 00/12] Line6 POD X3/X3Live suport Andrej Krutak
                     ` (10 preceding siblings ...)
  2016-09-16  9:12   ` [PATCH v3 11/12] ALSA: line6: Cleanup podhd initialization Andrej Krutak
@ 2016-09-16  9:12   ` Andrej Krutak
  11 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-09-16  9:12 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

We must do it this way, because e.g. POD X3 won't play any sound unless
the host listens on the bulk EP, so we cannot export it only via libusb.

The driver currently doesn't use the bulk EP messages in other way,
in future it could e.g. sense/modify volume(s).

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 include/uapi/sound/asound.h |   3 +-
 sound/usb/line6/driver.c    | 151 ++++++++++++++++++++++++++++++++++++++++++--
 sound/usb/line6/driver.h    |  23 ++++++-
 3 files changed, 170 insertions(+), 7 deletions(-)

diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index 609cadb..be353a7 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -106,9 +106,10 @@ enum {
 	SNDRV_HWDEP_IFACE_FW_OXFW,	/* Oxford OXFW970/971 based device */
 	SNDRV_HWDEP_IFACE_FW_DIGI00X,	/* Digidesign Digi 002/003 family */
 	SNDRV_HWDEP_IFACE_FW_TASCAM,	/* TASCAM FireWire series */
+	SNDRV_HWDEP_IFACE_LINE6,	/* Line6 USB processors */
 
 	/* Don't forget to change the following: */
-	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_TASCAM
+	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_LINE6
 };
 
 struct snd_hwdep_info {
diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 8a71d45..3536efe 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -17,6 +17,7 @@
 
 #include <sound/core.h>
 #include <sound/initval.h>
+#include <sound/hwdep.h>
 
 #include "capture.h"
 #include "driver.h"
@@ -303,7 +304,7 @@ static void line6_data_received(struct urb *urb)
 		for (;;) {
 			done =
 				line6_midibuf_read(mb, line6->buffer_message,
-						LINE6_MESSAGE_MAXLEN);
+						LINE6_MIDI_MESSAGE_MAXLEN);
 
 			if (done == 0)
 				break;
@@ -315,8 +316,11 @@ static void line6_data_received(struct urb *urb)
 				line6->process_message(line6);
 		}
 	} else {
+		line6->buffer_message = urb->transfer_buffer;
+		line6->message_length = urb->actual_length;
 		if (line6->process_message)
 			line6->process_message(line6);
+		line6->buffer_message = NULL;
 	}
 
 	line6_start_listen(line6);
@@ -473,14 +477,16 @@ static void line6_destruct(struct snd_card *card)
 	struct usb_line6 *line6 = card->private_data;
 	struct usb_device *usbdev = line6->usbdev;
 
-	/* free buffer memory first: */
-	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)
+	/* Free buffer memory first. We cannot depend on the existence of private
+	 * data from the (podhd) module, it may be gone already during this call */
+	if (line6->buffer_message)
 		kfree(line6->buffer_message);
 
 	kfree(line6->buffer_listen);
 
 	/* then free URBs: */
 	usb_free_urb(line6->urb_listen);
+	line6->urb_listen = NULL;
 
 	/* decrement reference counters: */
 	usb_put_dev(usbdev);
@@ -522,6 +528,138 @@ static void line6_get_interval(struct usb_line6 *line6)
 	}
 }
 
+
+/* Enable buffering of incoming messages, flush the buffer */
+static int line6_hwdep_open(struct snd_hwdep *hw, struct file *file)
+{
+	struct usb_line6 *line6 = hw->private_data;
+
+	/* NOTE: hwdep layer provides atomicity here */
+
+	line6->messages.active = 1;
+
+	return 0;
+}
+
+/* Stop buffering */
+static int line6_hwdep_release(struct snd_hwdep *hw, struct file *file)
+{
+	struct usb_line6 *line6 = hw->private_data;
+
+	line6->messages.active = 0;
+
+	return 0;
+}
+
+/* Read from circular buffer, return to user */
+static long
+line6_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+					loff_t *offset)
+{
+	struct usb_line6 *line6 = hwdep->private_data;
+	long rv = 0;
+	unsigned int out_count;
+
+	if (mutex_lock_interruptible(&line6->messages.read_lock))
+		return -ERESTARTSYS;
+
+	while (kfifo_len(&line6->messages.fifo) == 0) {
+		mutex_unlock(&line6->messages.read_lock);
+
+		rv = wait_event_interruptible(
+			line6->messages.wait_queue,
+			kfifo_len(&line6->messages.fifo) != 0);
+		if (rv < 0)
+			return rv;
+
+		if (mutex_lock_interruptible(&line6->messages.read_lock))
+			return -ERESTARTSYS;
+	}
+
+	if (kfifo_peek_len(&line6->messages.fifo) > count) {
+		/* Buffer too small; allow re-read of the current item... */
+		rv = -EINVAL;
+	} else {
+		rv = kfifo_to_user(&line6->messages.fifo, buf, count, &out_count);
+		if (rv == 0)
+			rv = out_count;
+	}
+
+	mutex_unlock(&line6->messages.read_lock);
+	return rv;
+}
+
+/* Write directly (no buffering) to device by user*/
+static long
+line6_hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count,
+					loff_t *offset)
+{
+	struct usb_line6 *line6 = hwdep->private_data;
+	int rv;
+	char *data_copy;
+
+	if (count > line6->max_packet_size * LINE6_RAW_MESSAGES_MAXCOUNT) {
+		/* This is an arbitrary limit - still better than nothing... */
+		return -EINVAL;
+	}
+
+	data_copy = memdup_user(data, count);
+	if (IS_ERR(ERR_PTR))
+		return -ENOMEM;
+
+	rv = line6_send_raw_message(line6, data_copy, count);
+
+	kfree(data_copy);
+	return rv;
+}
+
+static const struct snd_hwdep_ops hwdep_ops = {
+	.open    = line6_hwdep_open,
+	.release = line6_hwdep_release,
+	.read    = line6_hwdep_read,
+	.write   = line6_hwdep_write,
+};
+
+/* Insert into circular buffer */
+static void line6_hwdep_push_message(struct usb_line6 *line6)
+{
+	if (!line6->messages.active)
+		return;
+
+	if (kfifo_avail(&line6->messages.fifo) >= line6->message_length) {
+		/* No race condition here, there's only one writer */
+		kfifo_in(&line6->messages.fifo,
+			line6->buffer_message, line6->message_length);
+	} /* else TODO: signal overflow */
+
+	wake_up_interruptible(&line6->messages.wait_queue);
+}
+
+static int line6_hwdep_init(struct usb_line6 *line6)
+{
+	int err;
+	struct snd_hwdep *hwdep;
+
+	/* TODO: usb_driver_claim_interface(); */
+	line6->process_message = line6_hwdep_push_message;
+	line6->messages.active = 0;
+	init_waitqueue_head(&line6->messages.wait_queue);
+	mutex_init(&line6->messages.read_lock);
+	INIT_KFIFO(line6->messages.fifo);
+
+	err = snd_hwdep_new(line6->card, "config", 0, &hwdep);
+	if (err < 0)
+		goto end;
+	strcpy(hwdep->name, "config");
+	hwdep->iface = SNDRV_HWDEP_IFACE_LINE6;
+	hwdep->ops = hwdep_ops;
+	hwdep->private_data = line6;
+	hwdep->exclusive = true;
+
+end:
+	return err;
+}
+
 static int line6_init_cap_control(struct usb_line6 *line6)
 {
 	int ret;
@@ -536,9 +674,13 @@ static int line6_init_cap_control(struct usb_line6 *line6)
 		return -ENOMEM;
 
 	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
-		line6->buffer_message = kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL);
+		line6->buffer_message = kmalloc(LINE6_MIDI_MESSAGE_MAXLEN, GFP_KERNEL);
 		if (!line6->buffer_message)
 			return -ENOMEM;
+	} else {
+		ret = line6_hwdep_init(line6);
+		if (ret < 0)
+			return ret;
 	}
 
 	ret = line6_start_listen(line6);
@@ -716,3 +858,4 @@ EXPORT_SYMBOL_GPL(line6_resume);
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
+
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index d7eefe1..cf9f077 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -12,8 +12,9 @@
 #ifndef DRIVER_H
 #define DRIVER_H
 
-#include <linux/spinlock.h>
 #include <linux/usb.h>
+#include <linux/mutex.h>
+#include <linux/kfifo.h>
 #include <sound/core.h>
 
 #include "midi.h"
@@ -32,7 +33,16 @@
 
 #define LINE6_TIMEOUT 1
 #define LINE6_BUFSIZE_LISTEN 64
-#define LINE6_MESSAGE_MAXLEN 256
+#define LINE6_MIDI_MESSAGE_MAXLEN 256
+
+#define LINE6_RAW_MESSAGES_MAXCOUNT_ORDER 7
+/* 4k packets are common, BUFSIZE * MAXCOUNT should be bigger... */
+#define LINE6_RAW_MESSAGES_MAXCOUNT (1 << LINE6_RAW_MESSAGES_MAXCOUNT_ORDER)
+
+
+#if LINE6_BUFSIZE_LISTEN > 65535
+#error "Use dynamic fifo instead"
+#endif
 
 /*
 	Line 6 MIDI control commands
@@ -156,6 +166,15 @@ struct usb_line6 {
 	/* Length of message to be processed, generated from MIDI layer  */
 	int message_length;
 
+	/* Circular buffer for non-MIDI control messages */
+	struct {
+		struct mutex read_lock;
+		wait_queue_head_t wait_queue;
+		int active:1;
+		STRUCT_KFIFO_REC_2(LINE6_BUFSIZE_LISTEN * LINE6_RAW_MESSAGES_MAXCOUNT)
+			fifo;
+	} messages;
+
 	/* If MIDI is supported, buffer_message contains the pre-processed data;
 	 * otherwise the data is only in urb_listen (buffer_incoming). */
 	void (*process_message)(struct usb_line6 *);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* Re: [PATCH v3 03/12] ALSA: line6: Support assymetrical in/out configurations
  2016-09-16  9:12   ` [PATCH v3 03/12] ALSA: line6: Support assymetrical in/out configurations Andrej Krutak
@ 2016-09-16 18:34     ` kbuild test robot
  0 siblings, 0 replies; 81+ messages in thread
From: kbuild test robot @ 2016-09-16 18:34 UTC (permalink / raw)
  Cc: alsa-devel, stefanha, Andrej Krutak, tiwai, grabner, kbuild-all

[-- Attachment #1: Type: text/plain, Size: 3110 bytes --]

Hi Andrej,

[auto build test ERROR on sound/for-next]
[also build test ERROR on v4.8-rc6 next-20160916]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
[Suggest to use git(>=2.9.0) format-patch --base=<commit> (or --base=auto for convenience) to record what (public, well-known) commit your patch series was built on]
[Check https://git-scm.com/docs/git-format-patch for more information]

url:    https://github.com/0day-ci/linux/commits/Andrej-Krutak/Line6-POD-X3-X3Live-suport/20160916-210558
base:   https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git for-next
config: s390-allmodconfig (attached as .config)
compiler: s390x-linux-gnu-gcc (Debian 6.1.1-9) 6.1.1 20160705
reproduce:
        wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=s390 

Note: the linux-review/Andrej-Krutak/Line6-POD-X3-X3Live-suport/20160916-210558 HEAD 9ed07506e73b5835987ae6d7e2ba79e6c29dc6b6 builds fine.
      It only hurts bisectibility.

All errors (new ones prefixed by >>):

   sound/usb/line6/pcm.c: In function 'line6_pcm_acquire':
>> sound/usb/line6/pcm.c:297:9: error: too few arguments to function 'line6_buffer_acquire'
      ret = line6_buffer_acquire(line6pcm, pstr, type);
            ^~~~~~~~~~~~~~~~~~~~
   sound/usb/line6/pcm.c:151:12: note: declared here
    static int line6_buffer_acquire(struct snd_line6_pcm *line6pcm,
               ^~~~~~~~~~~~~~~~~~~~
   sound/usb/line6/pcm.c: In function 'snd_line6_hw_params':
   sound/usb/line6/pcm.c:342:8: error: too few arguments to function 'line6_buffer_acquire'
     ret = line6_buffer_acquire(line6pcm, pstr, LINE6_STREAM_PCM);
           ^~~~~~~~~~~~~~~~~~~~
   sound/usb/line6/pcm.c:151:12: note: declared here
    static int line6_buffer_acquire(struct snd_line6_pcm *line6pcm,
               ^~~~~~~~~~~~~~~~~~~~

vim +/line6_buffer_acquire +297 sound/usb/line6/pcm.c

63e20df1 Takashi Iwai 2015-01-27  291  	struct line6_pcm_stream *pstr;
63e20df1 Takashi Iwai 2015-01-27  292  	int ret = 0, dir;
63e20df1 Takashi Iwai 2015-01-27  293  
63e20df1 Takashi Iwai 2015-01-27  294  	mutex_lock(&line6pcm->state_mutex);
63e20df1 Takashi Iwai 2015-01-27  295  	for (dir = 0; dir < 2; dir++) {
63e20df1 Takashi Iwai 2015-01-27  296  		pstr = get_stream(line6pcm, dir);
63e20df1 Takashi Iwai 2015-01-27 @297  		ret = line6_buffer_acquire(line6pcm, pstr, type);
63e20df1 Takashi Iwai 2015-01-27  298  		if (ret < 0)
63e20df1 Takashi Iwai 2015-01-27  299  			goto error;
63e20df1 Takashi Iwai 2015-01-27  300  		if (!pstr->running)

:::::: The code at line 297 was first introduced by commit
:::::: 63e20df1e5b2ef8d871ecbdb6c038d554ed1ca74 ALSA: line6: Reorganize PCM stream handling

:::::: TO: Takashi Iwai <tiwai@suse.de>
:::::: CC: Takashi Iwai <tiwai@suse.de>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 42308 bytes --]

[-- Attachment #3: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 81+ messages in thread

* [PATCH v4 00/12] Line6 POD X3/X3Live suport
  2016-08-11 19:02 [PATCH 00/15] Line6 POD X3/X3Live suport Andrej Krutak
                   ` (17 preceding siblings ...)
  2016-09-16  9:12 ` [PATCH v3 00/12] Line6 POD X3/X3Live suport Andrej Krutak
@ 2016-09-18 18:59 ` Andrej Krutak
  2016-09-18 18:59   ` [PATCH v4 01/12] ALSA: line6: Enable different number of URBs for frame transfers Andrej Krutak
                     ` (12 more replies)
  18 siblings, 13 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-09-18 18:59 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

Hello all,


attached are the yet again reworked patches. Basically the diff is only some
formatting. In addition, some of the previously big patches were split to
multiple smaller ones. Compared to v3, there is a small bisectability/patch
sanity fix.

I have checked that after each one, the compilation works, but not that the
result doesn't segfault or even work - I don't have the hardware previously
supported by the driver and the most interesting changes are only useful as
a whole for POD X3.

Greetings!


Andrej Krutak (12):
  ALSA: line6: Enable different number of URBs for frame transfers
  ALSA: line6: Add high-speed USB support
  ALSA: line6: Support assymetrical in/out configurations
  ALSA: line6: Allow different channel numbers for in/out
  ALSA: line6: Add LINE6_CAP_IN_NEEDS_OUT, a void playback stream during
    capture
  ALSA: line6: Distinguish device init (ctrl EP) and MIDI data transfer
    (int EP)
  ALSA: line6: Allow processing of raw incoming messages
  ALSA: line6: Add support for POD X3
  ALSA: line6: Add support for POD X3 Live (only USB ID differs from POD
    X3)
  ALSA: line6: Only determine control port properties if needed
  ALSA: line6: Cleanup podhd initialization
  ALSA: line6: Add hwdep interface to access the POD control messages

 include/uapi/sound/asound.h |   3 +-
 sound/usb/line6/Kconfig     |   4 +-
 sound/usb/line6/capture.c   |  50 +++++---
 sound/usb/line6/driver.c    | 269 +++++++++++++++++++++++++++++++++------
 sound/usb/line6/driver.h    |  58 +++++++--
 sound/usb/line6/midi.c      |   2 +-
 sound/usb/line6/pcm.c       |  83 ++++++++----
 sound/usb/line6/pcm.h       |  19 +--
 sound/usb/line6/playback.c  |  37 ++++--
 sound/usb/line6/pod.c       |  12 +-
 sound/usb/line6/podhd.c     | 300 ++++++++++++++++++++++++++++++++++++++++----
 sound/usb/line6/toneport.c  |   6 +-
 sound/usb/line6/variax.c    |   6 +-
 13 files changed, 703 insertions(+), 146 deletions(-)

-- 
1.9.1

^ permalink raw reply	[flat|nested] 81+ messages in thread

* [PATCH v4 01/12] ALSA: line6: Enable different number of URBs for frame transfers
  2016-09-18 18:59 ` [PATCH v4 00/12] Line6 POD X3/X3Live suport Andrej Krutak
@ 2016-09-18 18:59   ` Andrej Krutak
  2016-09-18 18:59   ` [PATCH v4 02/12] ALSA: line6: Add high-speed USB support Andrej Krutak
                     ` (11 subsequent siblings)
  12 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-09-18 18:59 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

This basically changes LINE6_ISO_BUFFERS constant to a configurable
iso_buffers property.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/capture.c  | 17 +++++++++++------
 sound/usb/line6/driver.c   |  1 +
 sound/usb/line6/driver.h   |  2 ++
 sound/usb/line6/pcm.c      | 23 +++++++++++++++--------
 sound/usb/line6/pcm.h      |  2 +-
 sound/usb/line6/playback.c | 19 ++++++++++++-------
 6 files changed, 42 insertions(+), 22 deletions(-)

diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c
index f518fbb..e20a6bd 100644
--- a/sound/usb/line6/capture.c
+++ b/sound/usb/line6/capture.c
@@ -29,10 +29,10 @@ static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm)
 	int ret;
 	struct urb *urb_in;
 
-	index =
-	    find_first_zero_bit(&line6pcm->in.active_urbs, LINE6_ISO_BUFFERS);
+	index = find_first_zero_bit(&line6pcm->in.active_urbs,
+	                            line6pcm->line6->iso_buffers);
 
-	if (index < 0 || index >= LINE6_ISO_BUFFERS) {
+	if (index < 0 || index >= line6pcm->line6->iso_buffers) {
 		dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
 		return -EINVAL;
 	}
@@ -73,7 +73,7 @@ int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm)
 {
 	int ret = 0, i;
 
-	for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
+	for (i = 0; i < line6pcm->line6->iso_buffers; ++i) {
 		ret = submit_audio_in_urb(line6pcm);
 		if (ret < 0)
 			break;
@@ -154,7 +154,7 @@ static void audio_in_callback(struct urb *urb)
 	line6pcm->in.last_frame = urb->start_frame;
 
 	/* find index of URB */
-	for (index = 0; index < LINE6_ISO_BUFFERS; ++index)
+	for (index = 0; index < line6pcm->line6->iso_buffers; ++index)
 		if (urb == line6pcm->in.urbs[index])
 			break;
 
@@ -247,8 +247,13 @@ int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm)
 	struct usb_line6 *line6 = line6pcm->line6;
 	int i;
 
+	line6pcm->in.urbs = kzalloc(
+		sizeof(struct urb *) * line6->iso_buffers, GFP_KERNEL);
+	if (line6pcm->in.urbs == NULL)
+		return -ENOMEM;
+
 	/* create audio URBs and fill in constant values: */
-	for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
+	for (i = 0; i < line6->iso_buffers; ++i) {
 		struct urb *urb;
 
 		/* URB for audio in: */
diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 81b7da8..527c408 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -467,6 +467,7 @@ static void line6_get_interval(struct usb_line6 *line6)
 	unsigned epnum = usb_pipeendpoint(pipe);
 
 	ep = usbdev->ep_in[epnum];
+	line6->iso_buffers = LINE6_ISO_BUFFERS;
 	if (ep) {
 		line6->interval = ep->desc.bInterval;
 		line6->max_packet_size = le16_to_cpu(ep->desc.wMaxPacketSize);
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index 7da643e..43dd1d0 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -111,6 +111,8 @@ struct usb_line6 {
 
 	/* Interval (ms) */
 	int interval;
+	/* Number of isochronous URBs used for frame transfers */
+	int iso_buffers;
 
 	/* Maximum size of USB packet */
 	int max_packet_size;
diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c
index 41aa335..6c9b7cf 100644
--- a/sound/usb/line6/pcm.c
+++ b/sound/usb/line6/pcm.c
@@ -104,7 +104,7 @@ static void line6_unlink_audio_urbs(struct snd_line6_pcm *line6pcm,
 {
 	int i;
 
-	for (i = 0; i < LINE6_ISO_BUFFERS; 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]);
@@ -124,7 +124,7 @@ static void line6_wait_clear_audio_urbs(struct snd_line6_pcm *line6pcm,
 
 	do {
 		alive = 0;
-		for (i = 0; i < LINE6_ISO_BUFFERS; i++) {
+		for (i = 0; i < line6pcm->line6->iso_buffers; i++) {
 			if (test_bit(i, &pcms->active_urbs))
 				alive++;
 		}
@@ -153,8 +153,9 @@ static int line6_buffer_acquire(struct snd_line6_pcm *line6pcm,
 {
 	/* 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);
+		pstr->buffer = kmalloc(line6pcm->line6->iso_buffers *
+                                      LINE6_ISO_PACKETS *
+                                      line6pcm->max_packet_size, GFP_KERNEL);
 		if (!pstr->buffer)
 			return -ENOMEM;
 	}
@@ -434,24 +435,30 @@ static struct snd_kcontrol_new line6_controls[] = {
 /*
 	Cleanup the PCM device.
 */
-static void cleanup_urbs(struct line6_pcm_stream *pcms)
+static void cleanup_urbs(struct line6_pcm_stream *pcms, int iso_buffers)
 {
 	int i;
 
-	for (i = 0; i < LINE6_ISO_BUFFERS; i++) {
+	/* Most likely impossible in current code... */
+	if (pcms->urbs == NULL)
+		return;
+
+	for (i = 0; i < iso_buffers; i++) {
 		if (pcms->urbs[i]) {
 			usb_kill_urb(pcms->urbs[i]);
 			usb_free_urb(pcms->urbs[i]);
 		}
 	}
+	kfree(pcms->urbs);
+	pcms->urbs = NULL;
 }
 
 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);
+	cleanup_urbs(&line6pcm->out, line6pcm->line6->iso_buffers);
+	cleanup_urbs(&line6pcm->in, line6pcm->line6->iso_buffers);
 	kfree(line6pcm);
 }
 
diff --git a/sound/usb/line6/pcm.h b/sound/usb/line6/pcm.h
index 508410a..e983880 100644
--- a/sound/usb/line6/pcm.h
+++ b/sound/usb/line6/pcm.h
@@ -90,7 +90,7 @@ struct line6_pcm_properties {
 
 struct line6_pcm_stream {
 	/* allocated URBs */
-	struct urb *urbs[LINE6_ISO_BUFFERS];
+	struct urb **urbs;
 
 	/* Temporary buffer;
 	 * Since the packet size is not known in advance, this buffer is
diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c
index 97ed593..7a52806 100644
--- a/sound/usb/line6/playback.c
+++ b/sound/usb/line6/playback.c
@@ -154,10 +154,10 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
 		(USB_INTERVALS_PER_SECOND / LINE6_ISO_INTERVAL);
 	struct urb *urb_out;
 
-	index =
-	    find_first_zero_bit(&line6pcm->out.active_urbs, LINE6_ISO_BUFFERS);
+	index = find_first_zero_bit(&line6pcm->out.active_urbs,
+	                            line6pcm->line6->iso_buffers);
 
-	if (index < 0 || index >= LINE6_ISO_BUFFERS) {
+	if (index < 0 || index >= line6pcm->line6->iso_buffers) {
 		dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
 		return -EINVAL;
 	}
@@ -286,7 +286,7 @@ int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm)
 {
 	int ret = 0, i;
 
-	for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
+	for (i = 0; i < line6pcm->line6->iso_buffers; ++i) {
 		ret = submit_audio_out_urb(line6pcm);
 		if (ret < 0)
 			break;
@@ -313,11 +313,11 @@ static void audio_out_callback(struct urb *urb)
 	line6pcm->out.last_frame = urb->start_frame;
 
 	/* find index of URB */
-	for (index = 0; index < LINE6_ISO_BUFFERS; index++)
+	for (index = 0; index < line6pcm->line6->iso_buffers; index++)
 		if (urb == line6pcm->out.urbs[index])
 			break;
 
-	if (index >= LINE6_ISO_BUFFERS)
+	if (index >= line6pcm->line6->iso_buffers)
 		return;		/* URB has been unlinked asynchronously */
 
 	for (i = 0; i < LINE6_ISO_PACKETS; i++)
@@ -401,8 +401,13 @@ int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm)
 	struct usb_line6 *line6 = line6pcm->line6;
 	int i;
 
+	line6pcm->out.urbs = kzalloc(
+		sizeof(struct urb *) * line6->iso_buffers, GFP_KERNEL);
+	if (line6pcm->out.urbs == NULL)
+		return -ENOMEM;
+
 	/* create audio URBs and fill in constant values: */
-	for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
+	for (i = 0; i < line6->iso_buffers; ++i) {
 		struct urb *urb;
 
 		/* URB for audio out: */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v4 02/12] ALSA: line6: Add high-speed USB support
  2016-09-18 18:59 ` [PATCH v4 00/12] Line6 POD X3/X3Live suport Andrej Krutak
  2016-09-18 18:59   ` [PATCH v4 01/12] ALSA: line6: Enable different number of URBs for frame transfers Andrej Krutak
@ 2016-09-18 18:59   ` Andrej Krutak
  2016-09-18 18:59   ` [PATCH v4 03/12] ALSA: line6: Support assymetrical in/out configurations Andrej Krutak
                     ` (10 subsequent siblings)
  12 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-09-18 18:59 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

This has two parts:
* intervals_per_second setup
  (high speed needs 8000, instead of 1000)
* iso_buffers setup (count of iso buffers depends on
  USB speed, 2 is not enough for high speed)

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/capture.c  | 10 +++++++++-
 sound/usb/line6/driver.c   | 15 ++++++++++-----
 sound/usb/line6/driver.h   | 15 ++++++++++++---
 sound/usb/line6/pcm.h      |  6 ++----
 sound/usb/line6/playback.c |  2 +-
 5 files changed, 34 insertions(+), 14 deletions(-)

diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c
index e20a6bd..44e9a8c 100644
--- a/sound/usb/line6/capture.c
+++ b/sound/usb/line6/capture.c
@@ -181,7 +181,15 @@ static void audio_in_callback(struct urb *urb)
 
 		length += fsize;
 
-		/* the following assumes LINE6_ISO_PACKETS == 1: */
+		BUILD_BUG_ON_MSG(LINE6_ISO_PACKETS != 1,
+			"The following code assumes LINE6_ISO_PACKETS == 1");
+		/* TODO:
+		 * Also, if iso_buffers != 2, the prev frame is almost random at
+		 * playback side.
+		 * This needs to be redesigned. It should be "stable", but we may
+		 * experience sync problems on such high-speed configs.
+		 */
+
 		line6pcm->prev_fbuf = fbuf;
 		line6pcm->prev_fsize = fsize;
 
diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 527c408..14032d9 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -462,14 +462,18 @@ static void line6_destruct(struct snd_card *card)
 static void line6_get_interval(struct usb_line6 *line6)
 {
 	struct usb_device *usbdev = line6->usbdev;
-	struct usb_host_endpoint *ep;
-	unsigned pipe = usb_rcvintpipe(usbdev, line6->properties->ep_ctrl_r);
-	unsigned epnum = usb_pipeendpoint(pipe);
+	struct usb_host_endpoint *ep = usbdev->ep_in[line6->properties->ep_ctrl_r];
 
-	ep = usbdev->ep_in[epnum];
-	line6->iso_buffers = LINE6_ISO_BUFFERS;
 	if (ep) {
 		line6->interval = ep->desc.bInterval;
+		if (usbdev->speed == USB_SPEED_LOW) {
+			line6->intervals_per_second = USB_LOW_INTERVALS_PER_SECOND;
+			line6->iso_buffers = USB_LOW_ISO_BUFFERS;
+		} else {
+			line6->intervals_per_second = USB_HIGH_INTERVALS_PER_SECOND;
+			line6->iso_buffers = USB_HIGH_ISO_BUFFERS;
+		}
+
 		line6->max_packet_size = le16_to_cpu(ep->desc.wMaxPacketSize);
 	} else {
 		dev_err(line6->ifcdev,
@@ -559,6 +563,7 @@ int line6_probe(struct usb_interface *interface,
 	/* query interface number */
 	interface_number = interface->cur_altsetting->desc.bInterfaceNumber;
 
+	/* TODO reserves the bus bandwidth even without actual transfer */
 	ret = usb_set_interface(usbdev, interface_number,
 				properties->altsetting);
 	if (ret < 0) {
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index 43dd1d0..a55eb88 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -18,7 +18,13 @@
 
 #include "midi.h"
 
-#define USB_INTERVALS_PER_SECOND 1000
+/* USB 1.1 speed configuration */
+#define USB_LOW_INTERVALS_PER_SECOND 1000
+#define USB_LOW_ISO_BUFFERS 2
+
+/* USB 2.0+ speed configuration */
+#define USB_HIGH_INTERVALS_PER_SECOND 8000
+#define USB_HIGH_ISO_BUFFERS 16
 
 /* Fallback USB interval and max packet size values */
 #define LINE6_FALLBACK_INTERVAL 10
@@ -109,12 +115,15 @@ struct usb_line6 {
 	/* Properties */
 	const struct line6_properties *properties;
 
-	/* Interval (ms) */
+	/* Interval for data USB packets */
 	int interval;
+	/* ...for isochronous transfers framing */
+	int intervals_per_second;
+
 	/* Number of isochronous URBs used for frame transfers */
 	int iso_buffers;
 
-	/* Maximum size of USB packet */
+	/* Maximum size of data USB packet */
 	int max_packet_size;
 
 	/* Device representing the USB interface */
diff --git a/sound/usb/line6/pcm.h b/sound/usb/line6/pcm.h
index e983880..38bf4df 100644
--- a/sound/usb/line6/pcm.h
+++ b/sound/usb/line6/pcm.h
@@ -20,9 +20,6 @@
 
 #include "driver.h"
 
-/* number of URBs */
-#define LINE6_ISO_BUFFERS	2
-
 /*
 	number of USB frames per URB
 	The Line 6 Windows driver always transmits two frames per packet, but
@@ -31,7 +28,8 @@
 */
 #define LINE6_ISO_PACKETS	1
 
-/* in a "full speed" device (such as the PODxt Pro) this means 1ms */
+/* in a "full speed" device (such as the PODxt Pro) this means 1ms,
+   for "high speed" it's 1/8ms */
 #define LINE6_ISO_INTERVAL	1
 
 #define LINE6_IMPULSE_DEFAULT_PERIOD 100
diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c
index 7a52806..d53aad1 100644
--- a/sound/usb/line6/playback.c
+++ b/sound/usb/line6/playback.c
@@ -151,7 +151,7 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
 		line6pcm->properties->rates.rats[0].num_min;
 	const int frame_factor =
 		line6pcm->properties->rates.rats[0].den *
-		(USB_INTERVALS_PER_SECOND / LINE6_ISO_INTERVAL);
+		(line6pcm->line6->intervals_per_second / LINE6_ISO_INTERVAL);
 	struct urb *urb_out;
 
 	index = find_first_zero_bit(&line6pcm->out.active_urbs,
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v4 03/12] ALSA: line6: Support assymetrical in/out configurations
  2016-09-18 18:59 ` [PATCH v4 00/12] Line6 POD X3/X3Live suport Andrej Krutak
  2016-09-18 18:59   ` [PATCH v4 01/12] ALSA: line6: Enable different number of URBs for frame transfers Andrej Krutak
  2016-09-18 18:59   ` [PATCH v4 02/12] ALSA: line6: Add high-speed USB support Andrej Krutak
@ 2016-09-18 18:59   ` Andrej Krutak
  2016-09-18 18:59   ` [PATCH v4 04/12] ALSA: line6: Allow different channel numbers for in/out Andrej Krutak
                     ` (9 subsequent siblings)
  12 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-09-18 18:59 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

Splits max_packet_size to max_packet_size_in/out (e.g. for
different channel counts).

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/capture.c  | 10 +++++-----
 sound/usb/line6/pcm.c      | 33 +++++++++++++++++++--------------
 sound/usb/line6/pcm.h      |  5 +++--
 sound/usb/line6/playback.c |  2 +-
 4 files changed, 28 insertions(+), 22 deletions(-)

diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c
index 44e9a8c..73cea26 100644
--- a/sound/usb/line6/capture.c
+++ b/sound/usb/line6/capture.c
@@ -44,13 +44,13 @@ static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm)
 		struct usb_iso_packet_descriptor *fin =
 		    &urb_in->iso_frame_desc[i];
 		fin->offset = urb_size;
-		fin->length = line6pcm->max_packet_size;
-		urb_size += line6pcm->max_packet_size;
+		fin->length = line6pcm->max_packet_size_in;
+		urb_size += line6pcm->max_packet_size_in;
 	}
 
 	urb_in->transfer_buffer =
 	    line6pcm->in.buffer +
-	    index * LINE6_ISO_PACKETS * line6pcm->max_packet_size;
+	    index * LINE6_ISO_PACKETS * line6pcm->max_packet_size_in;
 	urb_in->transfer_buffer_length = urb_size;
 	urb_in->context = line6pcm;
 
@@ -173,10 +173,10 @@ static void audio_in_callback(struct urb *urb)
 		fbuf = urb->transfer_buffer + fin->offset;
 		fsize = fin->actual_length;
 
-		if (fsize > line6pcm->max_packet_size) {
+		if (fsize > line6pcm->max_packet_size_in) {
 			dev_err(line6pcm->line6->ifcdev,
 				"driver and/or device bug: packet too large (%d > %d)\n",
-				fsize, line6pcm->max_packet_size);
+				fsize, line6pcm->max_packet_size_in);
 		}
 
 		length += fsize;
diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c
index 6c9b7cf..31b68fa 100644
--- a/sound/usb/line6/pcm.c
+++ b/sound/usb/line6/pcm.c
@@ -146,16 +146,20 @@ get_stream(struct snd_line6_pcm *line6pcm, int direction)
 }
 
 /* allocate a buffer if not opened yet;
- * call this in line6pcm.state_change mutex
+ * call this in line6pcm.state_mutex
  */
 static int line6_buffer_acquire(struct snd_line6_pcm *line6pcm,
-				struct line6_pcm_stream *pstr, int type)
+				struct line6_pcm_stream *pstr, int direction, int type)
 {
+	const int 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(line6pcm->line6->iso_buffers *
-                                      LINE6_ISO_PACKETS *
-                                      line6pcm->max_packet_size, GFP_KERNEL);
+		                       LINE6_ISO_PACKETS * pkt_size, GFP_KERNEL);
 		if (!pstr->buffer)
 			return -ENOMEM;
 	}
@@ -163,12 +167,11 @@ static int line6_buffer_acquire(struct snd_line6_pcm *line6pcm,
 }
 
 /* free a buffer if all streams are closed;
- * call this in line6pcm.state_change mutex
+ * call this in line6pcm.state_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);
@@ -195,6 +198,7 @@ static int line6_stream_start(struct snd_line6_pcm *line6pcm, int direction,
 		else
 			ret = line6_submit_audio_in_all_urbs(line6pcm);
 	}
+
 	if (ret < 0)
 		clear_bit(type, &pstr->running);
 	spin_unlock_irqrestore(&pstr->lock, flags);
@@ -290,7 +294,7 @@ int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type)
 	mutex_lock(&line6pcm->state_mutex);
 	for (dir = 0; dir < 2; dir++) {
 		pstr = get_stream(line6pcm, dir);
-		ret = line6_buffer_acquire(line6pcm, pstr, type);
+		ret = line6_buffer_acquire(line6pcm, pstr, dir, type);
 		if (ret < 0)
 			goto error;
 		if (!pstr->running)
@@ -335,7 +339,8 @@ int snd_line6_hw_params(struct snd_pcm_substream *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);
+	ret = line6_buffer_acquire(line6pcm, pstr, substream->stream,
+	                           LINE6_STREAM_PCM);
 	if (ret < 0)
 		goto error;
 
@@ -530,12 +535,12 @@ int line6_init_pcm(struct usb_line6 *line6,
 	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));
+	line6pcm->max_packet_size_in =
+		usb_maxpacket(line6->usbdev,
+			usb_rcvisocpipe(line6->usbdev, ep_read), 0);
+	line6pcm->max_packet_size_out =
+		usb_maxpacket(line6->usbdev,
+			usb_sndisocpipe(line6->usbdev, ep_write), 1);
 
 	spin_lock_init(&line6pcm->out.lock);
 	spin_lock_init(&line6pcm->in.lock);
diff --git a/sound/usb/line6/pcm.h b/sound/usb/line6/pcm.h
index 38bf4df..f408d15 100644
--- a/sound/usb/line6/pcm.h
+++ b/sound/usb/line6/pcm.h
@@ -155,11 +155,12 @@ struct snd_line6_pcm {
 	/* Previously captured frame (for software monitoring) */
 	unsigned char *prev_fbuf;
 
-	/* Size of previously captured frame (for software monitoring) */
+	/* Size of previously captured frame (for software monitoring/sync) */
 	int prev_fsize;
 
 	/* Maximum size of USB packet */
-	int max_packet_size;
+	int max_packet_size_in;
+	int max_packet_size_out;
 
 	/* PCM playback volume (left and right) */
 	int volume_playback[2];
diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c
index d53aad1..7b2644f 100644
--- a/sound/usb/line6/playback.c
+++ b/sound/usb/line6/playback.c
@@ -195,7 +195,7 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
 	urb_frames = urb_size / bytes_per_frame;
 	urb_out->transfer_buffer =
 	    line6pcm->out.buffer +
-	    index * LINE6_ISO_PACKETS * line6pcm->max_packet_size;
+	    index * LINE6_ISO_PACKETS * line6pcm->max_packet_size_out;
 	urb_out->transfer_buffer_length = urb_size;
 	urb_out->context = line6pcm;
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v4 04/12] ALSA: line6: Allow different channel numbers for in/out
  2016-09-18 18:59 ` [PATCH v4 00/12] Line6 POD X3/X3Live suport Andrej Krutak
                     ` (2 preceding siblings ...)
  2016-09-18 18:59   ` [PATCH v4 03/12] ALSA: line6: Support assymetrical in/out configurations Andrej Krutak
@ 2016-09-18 18:59   ` Andrej Krutak
  2016-09-18 18:59   ` [PATCH v4 05/12] ALSA: line6: Add LINE6_CAP_IN_NEEDS_OUT, a void playback stream during capture Andrej Krutak
                     ` (8 subsequent siblings)
  12 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-09-18 18:59 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

Changes bytes_per_frame to bytes_per_channel.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/capture.c  |  8 ++++++--
 sound/usb/line6/driver.h   |  2 +-
 sound/usb/line6/pcm.h      |  2 +-
 sound/usb/line6/playback.c | 14 +++++++++++---
 sound/usb/line6/pod.c      |  3 +--
 sound/usb/line6/podhd.c    |  4 +---
 sound/usb/line6/toneport.c |  2 +-
 7 files changed, 22 insertions(+), 13 deletions(-)

diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c
index 73cea26..7959aaa 100644
--- a/sound/usb/line6/capture.c
+++ b/sound/usb/line6/capture.c
@@ -90,7 +90,9 @@ void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize)
 	struct snd_pcm_substream *substream =
 	    get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE);
 	struct snd_pcm_runtime *runtime = substream->runtime;
-	const int bytes_per_frame = line6pcm->properties->bytes_per_frame;
+	const int bytes_per_frame =
+		line6pcm->properties->bytes_per_channel *
+		line6pcm->properties->capture_hw.channels_max;
 	int frames = fsize / bytes_per_frame;
 
 	if (runtime == NULL)
@@ -191,7 +193,9 @@ static void audio_in_callback(struct urb *urb)
 		 */
 
 		line6pcm->prev_fbuf = fbuf;
-		line6pcm->prev_fsize = fsize;
+		line6pcm->prev_fsize = fsize /
+			(line6pcm->properties->bytes_per_channel *
+			line6pcm->properties->capture_hw.channels_max);
 
 		if (!test_bit(LINE6_STREAM_IMPULSE, &line6pcm->in.running) &&
 		    test_bit(LINE6_STREAM_PCM, &line6pcm->in.running) &&
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index a55eb88..2d32139 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -31,7 +31,7 @@
 #define LINE6_FALLBACK_MAXPACKETSIZE 16
 
 #define LINE6_TIMEOUT 1
-#define LINE6_BUFSIZE_LISTEN 32
+#define LINE6_BUFSIZE_LISTEN 64
 #define LINE6_MESSAGE_MAXLEN 256
 
 /*
diff --git a/sound/usb/line6/pcm.h b/sound/usb/line6/pcm.h
index f408d15..58d36f9 100644
--- a/sound/usb/line6/pcm.h
+++ b/sound/usb/line6/pcm.h
@@ -83,7 +83,7 @@ enum {
 struct line6_pcm_properties {
 	struct snd_pcm_hardware playback_hw, capture_hw;
 	struct snd_pcm_hw_constraint_ratdens rates;
-	int bytes_per_frame;
+	int bytes_per_channel;
 };
 
 struct line6_pcm_stream {
diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c
index 7b2644f..bc5799c 100644
--- a/sound/usb/line6/playback.c
+++ b/sound/usb/line6/playback.c
@@ -146,7 +146,9 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
 	int index;
 	int i, urb_size, urb_frames;
 	int ret;
-	const int bytes_per_frame = line6pcm->properties->bytes_per_frame;
+	const int bytes_per_frame =
+		line6pcm->properties->bytes_per_channel *
+		line6pcm->properties->playback_hw.channels_max;
 	const int frame_increment =
 		line6pcm->properties->rates.rats[0].num_min;
 	const int frame_factor =
@@ -165,6 +167,7 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
 	urb_out = line6pcm->out.urbs[index];
 	urb_size = 0;
 
+	/* TODO: this may not work for LINE6_ISO_PACKETS != 1 */
 	for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
 		/* compute frame size for given sampling rate */
 		int fsize = 0;
@@ -178,9 +181,11 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
 			line6pcm->out.count += frame_increment;
 			n = line6pcm->out.count / frame_factor;
 			line6pcm->out.count -= n * frame_factor;
-			fsize = n * bytes_per_frame;
+			fsize = n;
 		}
 
+		fsize *= bytes_per_frame;
+
 		fout->offset = urb_size;
 		fout->length = fsize;
 		urb_size += fsize;
@@ -305,6 +310,9 @@ static void audio_out_callback(struct urb *urb)
 	struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context;
 	struct snd_pcm_substream *substream =
 	    get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK);
+	const int bytes_per_frame =
+		line6pcm->properties->bytes_per_channel *
+		line6pcm->properties->playback_hw.channels_max;
 
 #if USE_CLEAR_BUFFER_WORKAROUND
 	memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);
@@ -329,7 +337,7 @@ static void audio_out_callback(struct urb *urb)
 		struct snd_pcm_runtime *runtime = substream->runtime;
 
 		line6pcm->out.pos_done +=
-		    length / line6pcm->properties->bytes_per_frame;
+		    length / bytes_per_frame;
 
 		if (line6pcm->out.pos_done >= runtime->buffer_size)
 			line6pcm->out.pos_done -= runtime->buffer_size;
diff --git a/sound/usb/line6/pod.c b/sound/usb/line6/pod.c
index 45dd348..36e7274 100644
--- a/sound/usb/line6/pod.c
+++ b/sound/usb/line6/pod.c
@@ -83,7 +83,6 @@ struct usb_line6_pod {
 };
 
 #define POD_SYSEX_CODE 3
-#define POD_BYTES_PER_FRAME 6	/* 24bit audio (stereo) */
 
 /* *INDENT-OFF* */
 
@@ -167,7 +166,7 @@ static struct line6_pcm_properties pod_pcm_properties = {
 	.rates = {
 			    .nrats = 1,
 			    .rats = &pod_ratden},
-	.bytes_per_frame = POD_BYTES_PER_FRAME
+	.bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */
 };
 
 static const char pod_version_header[] = {
diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c
index 63dcaef..4fc4789 100644
--- a/sound/usb/line6/podhd.c
+++ b/sound/usb/line6/podhd.c
@@ -25,8 +25,6 @@ enum {
 	LINE6_PODHD500_1,
 };
 
-#define PODHD_BYTES_PER_FRAME 6	/* 24bit audio (stereo) */
-
 static struct snd_ratden podhd_ratden = {
 	.num_min = 48000,
 	.num_max = 48000,
@@ -73,7 +71,7 @@ static struct line6_pcm_properties podhd_pcm_properties = {
 	.rates = {
 			    .nrats = 1,
 			    .rats = &podhd_ratden},
-	.bytes_per_frame = PODHD_BYTES_PER_FRAME
+	.bytes_per_channel = 3 /* 24bit audio (stereo) */
 };
 
 /*
diff --git a/sound/usb/line6/toneport.c b/sound/usb/line6/toneport.c
index 6d4c50c..da76e03 100644
--- a/sound/usb/line6/toneport.c
+++ b/sound/usb/line6/toneport.c
@@ -114,7 +114,7 @@ static struct line6_pcm_properties toneport_pcm_properties = {
 	.rates = {
 			    .nrats = 1,
 			    .rats = &toneport_ratden},
-	.bytes_per_frame = 4
+	.bytes_per_channel = 2
 };
 
 static const struct {
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v4 05/12] ALSA: line6: Add LINE6_CAP_IN_NEEDS_OUT, a void playback stream during capture
  2016-09-18 18:59 ` [PATCH v4 00/12] Line6 POD X3/X3Live suport Andrej Krutak
                     ` (3 preceding siblings ...)
  2016-09-18 18:59   ` [PATCH v4 04/12] ALSA: line6: Allow different channel numbers for in/out Andrej Krutak
@ 2016-09-18 18:59   ` Andrej Krutak
  2016-09-18 18:59   ` [PATCH v4 06/12] ALSA: line6: Distinguish device init (ctrl EP) and MIDI data transfer (int EP) Andrej Krutak
                     ` (7 subsequent siblings)
  12 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-09-18 18:59 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

E.g. POD X3 seems to require playback data to be sent to it to generate
capture data. Otherwise the device stalls and doesn't send any more capture
data until it's reset.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/capture.c  |  5 +++++
 sound/usb/line6/driver.h   |  4 +++-
 sound/usb/line6/pcm.c      | 31 ++++++++++++++++++++++++-------
 sound/usb/line6/pcm.h      |  4 +++-
 sound/usb/line6/toneport.c |  4 ++--
 5 files changed, 37 insertions(+), 11 deletions(-)

diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c
index 7959aaa..bf159c4 100644
--- a/sound/usb/line6/capture.c
+++ b/sound/usb/line6/capture.c
@@ -232,6 +232,8 @@ static int snd_line6_capture_open(struct snd_pcm_substream *substream)
 	if (err < 0)
 		return err;
 
+	line6_pcm_acquire(line6pcm, LINE6_STREAM_CAPTURE_HELPER, false);
+
 	runtime->hw = line6pcm->properties->capture_hw;
 	return 0;
 }
@@ -239,6 +241,9 @@ static int snd_line6_capture_open(struct snd_pcm_substream *substream)
 /* 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;
 }
 
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index 2d32139..0bcab38 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -100,8 +100,10 @@ enum {
 	LINE6_CAP_CONTROL =	1 << 0,
 	/* device supports PCM input/output via USB */
 	LINE6_CAP_PCM =		1 << 1,
-	/* device support hardware monitoring */
+	/* device supports hardware monitoring */
 	LINE6_CAP_HWMON =	1 << 2,
+	/* device requires output data when input is read */
+	LINE6_CAP_IN_NEEDS_OUT = 1 << 3,
 };
 
 /*
diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c
index 31b68fa..f0f3ff5 100644
--- a/sound/usb/line6/pcm.c
+++ b/sound/usb/line6/pcm.c
@@ -52,7 +52,7 @@ static int snd_line6_impulse_volume_put(struct snd_kcontrol *kcontrol,
 
 	line6pcm->impulse_volume = value;
 	if (value > 0) {
-		err = line6_pcm_acquire(line6pcm, LINE6_STREAM_IMPULSE);
+		err = line6_pcm_acquire(line6pcm, LINE6_STREAM_IMPULSE, true);
 		if (err < 0) {
 			line6pcm->impulse_volume = 0;
 			return err;
@@ -242,6 +242,14 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
 		switch (cmd) {
 		case SNDRV_PCM_TRIGGER_START:
 		case SNDRV_PCM_TRIGGER_RESUME:
+			if (s->stream == SNDRV_PCM_STREAM_CAPTURE &&
+				(line6pcm->line6->properties->capabilities &
+					LINE6_CAP_IN_NEEDS_OUT)) {
+				err = line6_stream_start(line6pcm, SNDRV_PCM_STREAM_PLAYBACK,
+						 LINE6_STREAM_CAPTURE_HELPER);
+				if (err < 0)
+					return err;
+			}
 			err = line6_stream_start(line6pcm, s->stream,
 						 LINE6_STREAM_PCM);
 			if (err < 0)
@@ -250,6 +258,12 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
 
 		case SNDRV_PCM_TRIGGER_STOP:
 		case SNDRV_PCM_TRIGGER_SUSPEND:
+			if (s->stream == SNDRV_PCM_STREAM_CAPTURE &&
+				(line6pcm->line6->properties->capabilities &
+					LINE6_CAP_IN_NEEDS_OUT)) {
+				line6_stream_stop(line6pcm, SNDRV_PCM_STREAM_PLAYBACK,
+					  LINE6_STREAM_CAPTURE_HELPER);
+			}
 			line6_stream_stop(line6pcm, s->stream,
 					  LINE6_STREAM_PCM);
 			break;
@@ -283,14 +297,15 @@ snd_pcm_uframes_t snd_line6_pointer(struct snd_pcm_substream *substream)
 	return pstr->pos_done;
 }
 
-/* Acquire and start duplex streams:
+/* 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)
+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);
@@ -300,10 +315,12 @@ int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type)
 		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;
+	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);
diff --git a/sound/usb/line6/pcm.h b/sound/usb/line6/pcm.h
index 58d36f9..5f796ef8 100644
--- a/sound/usb/line6/pcm.h
+++ b/sound/usb/line6/pcm.h
@@ -72,6 +72,7 @@ enum {
 	LINE6_STREAM_PCM,
 	LINE6_STREAM_MONITOR,
 	LINE6_STREAM_IMPULSE,
+	LINE6_STREAM_CAPTURE_HELPER,
 };
 
 /* misc bit flags for PCM operation */
@@ -190,7 +191,8 @@ extern int snd_line6_hw_params(struct snd_pcm_substream *substream,
 extern int snd_line6_hw_free(struct snd_pcm_substream *substream);
 extern snd_pcm_uframes_t snd_line6_pointer(struct snd_pcm_substream *substream);
 extern void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm);
-extern int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type);
+extern int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type,
+			       bool start);
 extern void line6_pcm_release(struct snd_line6_pcm *line6pcm, int type);
 
 #endif
diff --git a/sound/usb/line6/toneport.c b/sound/usb/line6/toneport.c
index da76e03..8e22f43 100644
--- a/sound/usb/line6/toneport.c
+++ b/sound/usb/line6/toneport.c
@@ -177,7 +177,7 @@ static int snd_toneport_monitor_put(struct snd_kcontrol *kcontrol,
 	line6pcm->volume_monitor = ucontrol->value.integer.value[0];
 
 	if (line6pcm->volume_monitor > 0) {
-		err = line6_pcm_acquire(line6pcm, LINE6_STREAM_MONITOR);
+		err = line6_pcm_acquire(line6pcm, LINE6_STREAM_MONITOR, true);
 		if (err < 0) {
 			line6pcm->volume_monitor = 0;
 			line6_pcm_release(line6pcm, LINE6_STREAM_MONITOR);
@@ -246,7 +246,7 @@ static void toneport_start_pcm(unsigned long arg)
 	struct usb_line6_toneport *toneport = (struct usb_line6_toneport *)arg;
 	struct usb_line6 *line6 = &toneport->line6;
 
-	line6_pcm_acquire(line6->line6pcm, LINE6_STREAM_MONITOR);
+	line6_pcm_acquire(line6->line6pcm, LINE6_STREAM_MONITOR, true);
 }
 
 /* control definition */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v4 06/12] ALSA: line6: Distinguish device init (ctrl EP) and MIDI data transfer (int EP)
  2016-09-18 18:59 ` [PATCH v4 00/12] Line6 POD X3/X3Live suport Andrej Krutak
                     ` (4 preceding siblings ...)
  2016-09-18 18:59   ` [PATCH v4 05/12] ALSA: line6: Add LINE6_CAP_IN_NEEDS_OUT, a void playback stream during capture Andrej Krutak
@ 2016-09-18 18:59   ` Andrej Krutak
  2016-09-18 18:59   ` [PATCH v4 07/12] ALSA: line6: Allow processing of raw incoming messages Andrej Krutak
                     ` (6 subsequent siblings)
  12 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-09-18 18:59 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

POD X3 can initialize similarly to older PODs, but it doesn't have the MIDI
interface. Instead, configuration is done via proprietary bulk EP messages.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/driver.c | 72 ++++++++++++++++++++++++++++++++++--------------
 sound/usb/line6/driver.h |  6 ++--
 sound/usb/line6/pod.c    |  9 +++++-
 sound/usb/line6/variax.c |  6 ++--
 4 files changed, 68 insertions(+), 25 deletions(-)

diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 14032d9..9b16777 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -66,10 +66,17 @@ static int line6_start_listen(struct usb_line6 *line6)
 {
 	int err;
 
-	usb_fill_int_urb(line6->urb_listen, line6->usbdev,
-		usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r),
-		line6->buffer_listen, LINE6_BUFSIZE_LISTEN,
-		line6_data_received, line6, line6->interval);
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+		usb_fill_int_urb(line6->urb_listen, line6->usbdev,
+			usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r),
+			line6->buffer_listen, LINE6_BUFSIZE_LISTEN,
+			line6_data_received, line6, line6->interval);
+	} else {
+		usb_fill_bulk_urb(line6->urb_listen, line6->usbdev,
+			usb_rcvbulkpipe(line6->usbdev, line6->properties->ep_ctrl_r),
+			line6->buffer_listen, LINE6_BUFSIZE_LISTEN,
+			line6_data_received, line6);
+	}
 	line6->urb_listen->actual_length = 0;
 	err = usb_submit_urb(line6->urb_listen, GFP_ATOMIC);
 	return err;
@@ -90,6 +97,7 @@ static int line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
 				  int size)
 {
 	int i, done = 0;
+	const struct line6_properties *properties = line6->properties;
 
 	for (i = 0; i < size; i += line6->max_packet_size) {
 		int partial;
@@ -97,15 +105,21 @@ static int line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
 		int frag_size = min(line6->max_packet_size, size - i);
 		int retval;
 
-		retval = usb_interrupt_msg(line6->usbdev,
-					usb_sndintpipe(line6->usbdev,
-						line6->properties->ep_ctrl_w),
-					(char *)frag_buf, frag_size,
-					&partial, LINE6_TIMEOUT * HZ);
+		if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+			retval = usb_interrupt_msg(line6->usbdev,
+						usb_sndintpipe(line6->usbdev, properties->ep_ctrl_w),
+						(char *)frag_buf, frag_size,
+						&partial, LINE6_TIMEOUT * HZ);
+		} else {
+			retval = usb_bulk_msg(line6->usbdev,
+						usb_sndbulkpipe(line6->usbdev, properties->ep_ctrl_w),
+						(char *)frag_buf, frag_size,
+						&partial, LINE6_TIMEOUT * HZ);
+		}
 
 		if (retval) {
 			dev_err(line6->ifcdev,
-				"usb_interrupt_msg failed (%d)\n", retval);
+				"usb_bulk_msg failed (%d)\n", retval);
 			break;
 		}
 
@@ -140,10 +154,17 @@ static int line6_send_raw_message_async_part(struct message *msg,
 	int done = msg->done;
 	int bytes = min(msg->size - done, line6->max_packet_size);
 
-	usb_fill_int_urb(urb, line6->usbdev,
-		usb_sndintpipe(line6->usbdev, line6->properties->ep_ctrl_w),
-		(char *)msg->buffer + done, bytes,
-		line6_async_request_sent, msg, line6->interval);
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+		usb_fill_int_urb(urb, line6->usbdev,
+			usb_sndintpipe(line6->usbdev, line6->properties->ep_ctrl_w),
+			(char *)msg->buffer + done, bytes,
+			line6_async_request_sent, msg, line6->interval);
+	} else {
+		usb_fill_bulk_urb(urb, line6->usbdev,
+			usb_sndbulkpipe(line6->usbdev, line6->properties->ep_ctrl_w),
+			(char *)msg->buffer + done, bytes,
+			line6_async_request_sent, msg);
+	}
 
 	msg->done += bytes;
 	retval = usb_submit_urb(urb, GFP_ATOMIC);
@@ -462,7 +483,18 @@ static void line6_destruct(struct snd_card *card)
 static void line6_get_interval(struct usb_line6 *line6)
 {
 	struct usb_device *usbdev = line6->usbdev;
-	struct usb_host_endpoint *ep = usbdev->ep_in[line6->properties->ep_ctrl_r];
+	const struct line6_properties *properties = line6->properties;
+	int pipe;
+	struct usb_host_endpoint *ep;
+
+	if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+		pipe =
+			usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r);
+	} else {
+		pipe =
+			usb_rcvbulkpipe(line6->usbdev, line6->properties->ep_ctrl_r);
+	}
+	ep = usbdev->ep_in[usb_pipeendpoint(pipe)];
 
 	if (ep) {
 		line6->interval = ep->desc.bInterval;
@@ -483,7 +515,7 @@ static void line6_get_interval(struct usb_line6 *line6)
 	}
 }
 
-static int line6_init_cap_control(struct usb_line6 *line6)
+static int line6_init_cap_control_midi(struct usb_line6 *line6)
 {
 	int ret;
 
@@ -573,8 +605,8 @@ int line6_probe(struct usb_interface *interface,
 
 	line6_get_interval(line6);
 
-	if (properties->capabilities & LINE6_CAP_CONTROL) {
-		ret = line6_init_cap_control(line6);
+	if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+		ret = line6_init_cap_control_midi(line6);
 		if (ret < 0)
 			goto error;
 	}
@@ -644,7 +676,7 @@ int line6_suspend(struct usb_interface *interface, pm_message_t message)
 
 	snd_power_change_state(line6->card, SNDRV_CTL_POWER_D3hot);
 
-	if (line6->properties->capabilities & LINE6_CAP_CONTROL)
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)
 		line6_stop_listen(line6);
 
 	if (line6pcm != NULL) {
@@ -663,7 +695,7 @@ int line6_resume(struct usb_interface *interface)
 {
 	struct usb_line6 *line6 = usb_get_intfdata(interface);
 
-	if (line6->properties->capabilities & LINE6_CAP_CONTROL)
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)
 		line6_start_listen(line6);
 
 	snd_power_change_state(line6->card, SNDRV_CTL_POWER_D0);
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index 0bcab38..d48c7d2 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -104,6 +104,8 @@ enum {
 	LINE6_CAP_HWMON =	1 << 2,
 	/* device requires output data when input is read */
 	LINE6_CAP_IN_NEEDS_OUT = 1 << 3,
+	/* device uses raw MIDI via USB (data endpoints) */
+	LINE6_CAP_CONTROL_MIDI = 1 << 4,
 };
 
 /*
@@ -142,10 +144,10 @@ struct usb_line6 {
 	/* Line 6 MIDI device data structure */
 	struct snd_line6_midi *line6midi;
 
-	/* URB for listening to PODxt Pro control endpoint */
+	/* URB for listening to POD data endpoint */
 	struct urb *urb_listen;
 
-	/* Buffer for listening to PODxt Pro control endpoint */
+	/* Buffer for listening to POD data endpoint */
 	unsigned char *buffer_listen;
 
 	/* Buffer for message to be processed */
diff --git a/sound/usb/line6/pod.c b/sound/usb/line6/pod.c
index 36e7274..17aa616 100644
--- a/sound/usb/line6/pod.c
+++ b/sound/usb/line6/pod.c
@@ -475,6 +475,7 @@ static const struct line6_properties pod_properties_table[] = {
 		.id = "BassPODxt",
 		.name = "BassPODxt",
 		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI
 				| LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 5,
@@ -487,6 +488,7 @@ static const struct line6_properties pod_properties_table[] = {
 		.id = "BassPODxtLive",
 		.name = "BassPODxt Live",
 		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI
 				| LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 1,
@@ -499,6 +501,7 @@ static const struct line6_properties pod_properties_table[] = {
 		.id = "BassPODxtPro",
 		.name = "BassPODxt Pro",
 		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI
 				| LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 5,
@@ -510,7 +513,8 @@ static const struct line6_properties pod_properties_table[] = {
 	[LINE6_POCKETPOD] = {
 		.id = "PocketPOD",
 		.name = "Pocket POD",
-		.capabilities	= LINE6_CAP_CONTROL,
+		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI,
 		.altsetting = 0,
 		.ep_ctrl_r = 0x82,
 		.ep_ctrl_w = 0x02,
@@ -520,6 +524,7 @@ static const struct line6_properties pod_properties_table[] = {
 		.id = "PODxt",
 		.name = "PODxt",
 		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI
 				| LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 5,
@@ -532,6 +537,7 @@ static const struct line6_properties pod_properties_table[] = {
 		.id = "PODxtLive",
 		.name = "PODxt Live",
 		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI
 				| LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 1,
@@ -544,6 +550,7 @@ static const struct line6_properties pod_properties_table[] = {
 		.id = "PODxtPro",
 		.name = "PODxt Pro",
 		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI
 				| LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 5,
diff --git a/sound/usb/line6/variax.c b/sound/usb/line6/variax.c
index ddc23dd..0c4512d 100644
--- a/sound/usb/line6/variax.c
+++ b/sound/usb/line6/variax.c
@@ -259,7 +259,8 @@ static const struct line6_properties variax_properties_table[] = {
 	[LINE6_PODXTLIVE_VARIAX] = {
 		.id = "PODxtLive",
 		.name = "PODxt Live",
-		.capabilities	= LINE6_CAP_CONTROL,
+		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI,
 		.altsetting = 1,
 		.ep_ctrl_r = 0x86,
 		.ep_ctrl_w = 0x05,
@@ -269,7 +270,8 @@ static const struct line6_properties variax_properties_table[] = {
 	[LINE6_VARIAX] = {
 		.id = "Variax",
 		.name = "Variax Workbench",
-		.capabilities	= LINE6_CAP_CONTROL,
+		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_CONTROL_MIDI,
 		.altsetting = 1,
 		.ep_ctrl_r = 0x82,
 		.ep_ctrl_w = 0x01,
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v4 07/12] ALSA: line6: Allow processing of raw incoming messages
  2016-09-18 18:59 ` [PATCH v4 00/12] Line6 POD X3/X3Live suport Andrej Krutak
                     ` (5 preceding siblings ...)
  2016-09-18 18:59   ` [PATCH v4 06/12] ALSA: line6: Distinguish device init (ctrl EP) and MIDI data transfer (int EP) Andrej Krutak
@ 2016-09-18 18:59   ` Andrej Krutak
  2016-09-18 18:59   ` [PATCH v4 08/12] ALSA: line6: Add support for POD X3 Andrej Krutak
                     ` (5 subsequent siblings)
  12 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-09-18 18:59 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

Not all PODs use MIDI via USB data interface, thus allow avoiding
that code and instead using direct processing.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/driver.c | 59 ++++++++++++++++++++++++++++--------------------
 sound/usb/line6/driver.h |  8 ++++---
 sound/usb/line6/midi.c   |  2 +-
 3 files changed, 40 insertions(+), 29 deletions(-)

diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 9b16777..853a143 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -290,26 +290,31 @@ static void line6_data_received(struct urb *urb)
 	if (urb->status == -ESHUTDOWN)
 		return;
 
-	done =
-	    line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length);
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+		done =
+			line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length);
 
-	if (done < urb->actual_length) {
-		line6_midibuf_ignore(mb, done);
-		dev_dbg(line6->ifcdev, "%d %d buffer overflow - message skipped\n",
-			done, urb->actual_length);
-	}
+		if (done < urb->actual_length) {
+			line6_midibuf_ignore(mb, done);
+			dev_dbg(line6->ifcdev, "%d %d buffer overflow - message skipped\n",
+				done, urb->actual_length);
+		}
 
-	for (;;) {
-		done =
-		    line6_midibuf_read(mb, line6->buffer_message,
-				       LINE6_MESSAGE_MAXLEN);
+		for (;;) {
+			done =
+				line6_midibuf_read(mb, line6->buffer_message,
+						LINE6_MESSAGE_MAXLEN);
 
-		if (done == 0)
-			break;
+			if (done == 0)
+				break;
 
-		line6->message_length = done;
-		line6_midi_receive(line6, line6->buffer_message, done);
+			line6->message_length = done;
+			line6_midi_receive(line6, line6->buffer_message, done);
 
+			if (line6->process_message)
+				line6->process_message(line6);
+		}
+	} else {
 		if (line6->process_message)
 			line6->process_message(line6);
 	}
@@ -469,7 +474,9 @@ static void line6_destruct(struct snd_card *card)
 	struct usb_device *usbdev = line6->usbdev;
 
 	/* free buffer memory first: */
-	kfree(line6->buffer_message);
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)
+		kfree(line6->buffer_message);
+
 	kfree(line6->buffer_listen);
 
 	/* then free URBs: */
@@ -515,7 +522,7 @@ static void line6_get_interval(struct usb_line6 *line6)
 	}
 }
 
-static int line6_init_cap_control_midi(struct usb_line6 *line6)
+static int line6_init_cap_control(struct usb_line6 *line6)
 {
 	int ret;
 
@@ -524,14 +531,16 @@ static int line6_init_cap_control_midi(struct usb_line6 *line6)
 	if (!line6->buffer_listen)
 		return -ENOMEM;
 
-	line6->buffer_message = kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL);
-	if (!line6->buffer_message)
-		return -ENOMEM;
-
 	line6->urb_listen = usb_alloc_urb(0, GFP_KERNEL);
 	if (!line6->urb_listen)
 		return -ENOMEM;
 
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+		line6->buffer_message = kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL);
+		if (!line6->buffer_message)
+			return -ENOMEM;
+	}
+
 	ret = line6_start_listen(line6);
 	if (ret < 0) {
 		dev_err(line6->ifcdev, "cannot start listening: %d\n", ret);
@@ -605,8 +614,8 @@ int line6_probe(struct usb_interface *interface,
 
 	line6_get_interval(line6);
 
-	if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
-		ret = line6_init_cap_control_midi(line6);
+	if (properties->capabilities & LINE6_CAP_CONTROL) {
+		ret = line6_init_cap_control(line6);
 		if (ret < 0)
 			goto error;
 	}
@@ -676,7 +685,7 @@ int line6_suspend(struct usb_interface *interface, pm_message_t message)
 
 	snd_power_change_state(line6->card, SNDRV_CTL_POWER_D3hot);
 
-	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL)
 		line6_stop_listen(line6);
 
 	if (line6pcm != NULL) {
@@ -695,7 +704,7 @@ int line6_resume(struct usb_interface *interface)
 {
 	struct usb_line6 *line6 = usb_get_intfdata(interface);
 
-	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)
+	if (line6->properties->capabilities & LINE6_CAP_CONTROL)
 		line6_start_listen(line6);
 
 	snd_power_change_state(line6->card, SNDRV_CTL_POWER_D0);
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index d48c7d2..d7eefe1 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -147,15 +147,17 @@ struct usb_line6 {
 	/* URB for listening to POD data endpoint */
 	struct urb *urb_listen;
 
-	/* Buffer for listening to POD data endpoint */
+	/* Buffer for incoming data from POD data endpoint */
 	unsigned char *buffer_listen;
 
-	/* Buffer for message to be processed */
+	/* Buffer for message to be processed, generated from MIDI layer */
 	unsigned char *buffer_message;
 
-	/* Length of message to be processed */
+	/* Length of message to be processed, generated from MIDI layer  */
 	int message_length;
 
+	/* If MIDI is supported, buffer_message contains the pre-processed data;
+	 * otherwise the data is only in urb_listen (buffer_incoming). */
 	void (*process_message)(struct usb_line6 *);
 	void (*disconnect)(struct usb_line6 *line6);
 };
diff --git a/sound/usb/line6/midi.c b/sound/usb/line6/midi.c
index cebea9b..d0fb2f2 100644
--- a/sound/usb/line6/midi.c
+++ b/sound/usb/line6/midi.c
@@ -258,7 +258,7 @@ int line6_init_midi(struct usb_line6 *line6)
 	struct snd_rawmidi *rmidi;
 	struct snd_line6_midi *line6midi;
 
-	if (!(line6->properties->capabilities & LINE6_CAP_CONTROL)) {
+	if (!(line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)) {
 		/* skip MIDI initialization and report success */
 		return 0;
 	}
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v4 08/12] ALSA: line6: Add support for POD X3
  2016-09-18 18:59 ` [PATCH v4 00/12] Line6 POD X3/X3Live suport Andrej Krutak
                     ` (6 preceding siblings ...)
  2016-09-18 18:59   ` [PATCH v4 07/12] ALSA: line6: Allow processing of raw incoming messages Andrej Krutak
@ 2016-09-18 18:59   ` Andrej Krutak
  2016-09-18 18:59   ` [PATCH v4 09/12] ALSA: line6: Add support for POD X3 Live (only USB ID differs from POD X3) Andrej Krutak
                     ` (4 subsequent siblings)
  12 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-09-18 18:59 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

This includes audio in/out and basic initialization via control EP (emulates
what original driver does). The initialization is done similarly to original
POD, firmware and serial IDs are read and exported via sysfs.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/Kconfig |   4 +-
 sound/usb/line6/podhd.c | 276 +++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 265 insertions(+), 15 deletions(-)

diff --git a/sound/usb/line6/Kconfig b/sound/usb/line6/Kconfig
index f4585d37..8ffcf48 100644
--- a/sound/usb/line6/Kconfig
+++ b/sound/usb/line6/Kconfig
@@ -21,10 +21,10 @@ config SND_USB_POD
 	      re-amping)
 
 config SND_USB_PODHD
-	tristate "Line 6 POD HD300/400/500 USB support"
+	tristate "Line 6 POD X3/HD300/400/500 USB support"
 	select SND_USB_LINE6
 	help
-	  This is a driver for POD HD300, 400 and 500 devices.
+	  This is a driver for POD X3, HD300, 400 and 500 devices.
 
 config SND_USB_TONEPORT
 	tristate "TonePort GX, UX1 and UX2 USB support"
diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c
index 4fc4789..8cf0a98 100644
--- a/sound/usb/line6/podhd.c
+++ b/sound/usb/line6/podhd.c
@@ -2,6 +2,7 @@
  * Line 6 Pod HD
  *
  * Copyright (C) 2011 Stefan Hajnoczi <stefanha@gmail.com>
+ * Copyright (C) 2015 Andrej Krutak <dev@andree.sk>
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License as
@@ -18,11 +19,44 @@
 #include "driver.h"
 #include "pcm.h"
 
+#define PODHD_STARTUP_DELAY 500
+
+/*
+ * Stages of POD startup procedure
+ */
+enum {
+	PODHD_STARTUP_INIT = 1,
+	PODHD_STARTUP_SCHEDULE_WORKQUEUE,
+	PODHD_STARTUP_SETUP,
+	PODHD_STARTUP_LAST = PODHD_STARTUP_SETUP - 1
+};
+
 enum {
 	LINE6_PODHD300,
 	LINE6_PODHD400,
 	LINE6_PODHD500_0,
 	LINE6_PODHD500_1,
+	LINE6_PODX3,
+};
+
+struct usb_line6_podhd {
+	/* Generic Line 6 USB data */
+	struct usb_line6 line6;
+
+	/* Timer for device initialization */
+	struct timer_list startup_timer;
+
+	/* Work handler for device initialization */
+	struct work_struct startup_work;
+
+	/* Current progress in startup procedure */
+	int startup_progress;
+
+	/* Serial number of device */
+	u32 serial_number;
+
+	/* Firmware version */
+	int firmware_version;
 };
 
 static struct snd_ratden podhd_ratden = {
@@ -71,9 +105,196 @@ static struct line6_pcm_properties podhd_pcm_properties = {
 	.rates = {
 			    .nrats = 1,
 			    .rats = &podhd_ratden},
-	.bytes_per_channel = 3 /* 24bit audio (stereo) */
+	.bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */
 };
 
+static struct line6_pcm_properties podx3_pcm_properties = {
+	.playback_hw = {
+				  .info = (SNDRV_PCM_INFO_MMAP |
+					   SNDRV_PCM_INFO_INTERLEAVED |
+					   SNDRV_PCM_INFO_BLOCK_TRANSFER |
+					   SNDRV_PCM_INFO_MMAP_VALID |
+					   SNDRV_PCM_INFO_PAUSE |
+					   SNDRV_PCM_INFO_SYNC_START),
+				  .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+				  .rates = SNDRV_PCM_RATE_48000,
+				  .rate_min = 48000,
+				  .rate_max = 48000,
+				  .channels_min = 2,
+				  .channels_max = 2,
+				  .buffer_bytes_max = 60000,
+				  .period_bytes_min = 64,
+				  .period_bytes_max = 8192,
+				  .periods_min = 1,
+				  .periods_max = 1024},
+	.capture_hw = {
+				 .info = (SNDRV_PCM_INFO_MMAP |
+					  SNDRV_PCM_INFO_INTERLEAVED |
+					  SNDRV_PCM_INFO_BLOCK_TRANSFER |
+					  SNDRV_PCM_INFO_MMAP_VALID |
+					  SNDRV_PCM_INFO_SYNC_START),
+				 .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+				 .rates = SNDRV_PCM_RATE_48000,
+				 .rate_min = 48000,
+				 .rate_max = 48000,
+				 /* 1+2: Main signal (out), 3+4: Tone 1,
+				  * 5+6: Tone 2, 7+8: raw */
+				 .channels_min = 8,
+				 .channels_max = 8,
+				 .buffer_bytes_max = 60000,
+				 .period_bytes_min = 64,
+				 .period_bytes_max = 8192,
+				 .periods_min = 1,
+				 .periods_max = 1024},
+	.rates = {
+			    .nrats = 1,
+			    .rats = &podhd_ratden},
+	.bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */
+};
+
+static void podhd_startup_start_workqueue(unsigned long data);
+static void podhd_startup_workqueue(struct work_struct *work);
+static int podhd_startup_finalize(struct usb_line6_podhd *pod);
+
+static ssize_t serial_number_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct snd_card *card = dev_to_snd_card(dev);
+	struct usb_line6_podhd *pod = card->private_data;
+
+	return sprintf(buf, "%u\n", pod->serial_number);
+}
+
+static ssize_t firmware_version_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct snd_card *card = dev_to_snd_card(dev);
+	struct usb_line6_podhd *pod = card->private_data;
+
+	return sprintf(buf, "%06x\n", pod->firmware_version);
+}
+
+static DEVICE_ATTR_RO(firmware_version);
+static DEVICE_ATTR_RO(serial_number);
+
+static struct attribute *podhd_dev_attrs[] = {
+	&dev_attr_firmware_version.attr,
+	&dev_attr_serial_number.attr,
+	NULL
+};
+
+static const struct attribute_group podhd_dev_attr_group = {
+	.name = "podhd",
+	.attrs = podhd_dev_attrs,
+};
+
+/*
+ * POD X3 startup procedure.
+ *
+ * May be compatible with other POD HD's, since it's also similar to the
+ * previous POD setup. In any case, it doesn't seem to be required for the
+ * audio nor bulk interfaces to work.
+ */
+
+static void podhd_startup(struct usb_line6_podhd *pod)
+{
+	CHECK_STARTUP_PROGRESS(pod->startup_progress, PODHD_STARTUP_INIT);
+
+	/* delay startup procedure: */
+	line6_start_timer(&pod->startup_timer, PODHD_STARTUP_DELAY,
+		podhd_startup_start_workqueue, (unsigned long)pod);
+}
+
+static void podhd_startup_start_workqueue(unsigned long data)
+{
+	struct usb_line6_podhd *pod = (struct usb_line6_podhd *)data;
+
+	CHECK_STARTUP_PROGRESS(pod->startup_progress,
+		PODHD_STARTUP_SCHEDULE_WORKQUEUE);
+
+	/* schedule work for global work queue: */
+	schedule_work(&pod->startup_work);
+}
+
+static int podhd_dev_start(struct usb_line6_podhd *pod)
+{
+	int ret;
+	u8 init_bytes[8];
+	int i;
+	struct usb_device *usbdev = pod->line6.usbdev;
+
+	ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
+					0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+					0x11, 0,
+					NULL, 0, LINE6_TIMEOUT * HZ);
+	if (ret < 0) {
+		dev_err(pod->line6.ifcdev, "read request failed (error %d)\n", ret);
+		return ret;
+	}
+
+	/* NOTE: looks like some kind of ping message */
+	ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67,
+					USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+					0x11, 0x0,
+					&init_bytes, 3, LINE6_TIMEOUT * HZ);
+	if (ret < 0) {
+		dev_err(pod->line6.ifcdev,
+			"receive length failed (error %d)\n", ret);
+		return ret;
+	}
+
+	pod->firmware_version =
+		(init_bytes[0] << 16) | (init_bytes[1] << 8) | (init_bytes[2] << 0);
+
+	for (i = 0; i <= 16; i++) {
+		ret = line6_read_data(&pod->line6, 0xf000 + 0x08 * i, init_bytes, 8);
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
+					USB_REQ_SET_FEATURE,
+					USB_TYPE_STANDARD | USB_RECIP_DEVICE | USB_DIR_OUT,
+					1, 0,
+					NULL, 0, LINE6_TIMEOUT * HZ);
+	if (ret < 0) {
+		return ret;
+	}
+
+	return 0;
+}
+
+static void podhd_startup_workqueue(struct work_struct *work)
+{
+	struct usb_line6_podhd *pod =
+	    container_of(work, struct usb_line6_podhd, startup_work);
+
+	CHECK_STARTUP_PROGRESS(pod->startup_progress, PODHD_STARTUP_SETUP);
+
+	podhd_dev_start(pod);
+	line6_read_serial_number(&pod->line6, &pod->serial_number);
+
+	podhd_startup_finalize(pod);
+}
+
+static int podhd_startup_finalize(struct usb_line6_podhd *pod)
+{
+	struct usb_line6 *line6 = &pod->line6;
+
+	/* ALSA audio interface: */
+	return snd_card_register(line6->card);
+}
+
+static void podhd_disconnect(struct usb_line6 *line6)
+{
+	struct usb_line6_podhd *pod = (struct usb_line6_podhd *)line6;
+
+	if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) {
+		del_timer_sync(&pod->startup_timer);
+		cancel_work_sync(&pod->startup_work);
+	}
+}
+
 /*
 	Try to init POD HD device.
 */
@@ -81,6 +302,16 @@ static int podhd_init(struct usb_line6 *line6,
 		      const struct usb_device_id *id)
 {
 	int err;
+	struct usb_line6_podhd *pod = (struct usb_line6_podhd *) line6;
+
+	line6->disconnect = podhd_disconnect;
+
+	if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) {
+		/* create sysfs entries: */
+		err = snd_card_add_dev_attr(line6->card, &podhd_dev_attr_group);
+		if (err < 0)
+			return err;
+	}
 
 	/* initialize MIDI subsystem: */
 	err = line6_init_midi(line6);
@@ -88,12 +319,22 @@ static int podhd_init(struct usb_line6 *line6,
 		return err;
 
 	/* initialize PCM subsystem: */
-	err = line6_init_pcm(line6, &podhd_pcm_properties);
+	err = line6_init_pcm(line6,
+		(id->driver_info == LINE6_PODX3) ? &podx3_pcm_properties :
+		&podhd_pcm_properties);
 	if (err < 0)
 		return err;
 
-	/* register USB audio system: */
-	return snd_card_register(line6->card);
+	if (!(pod->line6.properties->capabilities & LINE6_CAP_CONTROL)) {
+		/* register USB audio system directly */
+		return podhd_startup_finalize(pod);
+	}
+
+	/* init device and delay registering */
+	init_timer(&pod->startup_timer);
+	INIT_WORK(&pod->startup_work, podhd_startup_workqueue);
+	podhd_startup(pod);
+	return 0;
 }
 
 #define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod)
@@ -101,10 +342,12 @@ static int podhd_init(struct usb_line6 *line6,
 
 /* table of devices that work with this driver */
 static const struct usb_device_id podhd_id_table[] = {
+	/* TODO: no need to alloc data interfaces when only audio is used */
 	{ LINE6_DEVICE(0x5057),    .driver_info = LINE6_PODHD300 },
 	{ LINE6_DEVICE(0x5058),    .driver_info = LINE6_PODHD400 },
 	{ LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500_0 },
 	{ LINE6_IF_NUM(0x414D, 1), .driver_info = LINE6_PODHD500_1 },
+	{ LINE6_IF_NUM(0x414A, 0), .driver_info = LINE6_PODX3 },
 	{}
 };
 
@@ -114,8 +357,7 @@ static const struct line6_properties podhd_properties_table[] = {
 	[LINE6_PODHD300] = {
 		.id = "PODHD300",
 		.name = "POD HD300",
-		.capabilities	= LINE6_CAP_CONTROL
-				| LINE6_CAP_PCM
+		.capabilities	= LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 5,
 		.ep_ctrl_r = 0x84,
@@ -126,8 +368,7 @@ static const struct line6_properties podhd_properties_table[] = {
 	[LINE6_PODHD400] = {
 		.id = "PODHD400",
 		.name = "POD HD400",
-		.capabilities	= LINE6_CAP_CONTROL
-				| LINE6_CAP_PCM
+		.capabilities	= LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 5,
 		.ep_ctrl_r = 0x84,
@@ -138,8 +379,7 @@ static const struct line6_properties podhd_properties_table[] = {
 	[LINE6_PODHD500_0] = {
 		.id = "PODHD500",
 		.name = "POD HD500",
-		.capabilities	= LINE6_CAP_CONTROL
-				| LINE6_CAP_PCM
+		.capabilities	= LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 1,
 		.ep_ctrl_r = 0x81,
@@ -150,8 +390,7 @@ static const struct line6_properties podhd_properties_table[] = {
 	[LINE6_PODHD500_1] = {
 		.id = "PODHD500",
 		.name = "POD HD500",
-		.capabilities	= LINE6_CAP_CONTROL
-				| LINE6_CAP_PCM
+		.capabilities	= LINE6_CAP_PCM
 				| LINE6_CAP_HWMON,
 		.altsetting = 1,
 		.ep_ctrl_r = 0x81,
@@ -159,6 +398,17 @@ static const struct line6_properties podhd_properties_table[] = {
 		.ep_audio_r = 0x86,
 		.ep_audio_w = 0x02,
 	},
+	[LINE6_PODX3] = {
+		.id = "PODX3",
+		.name = "POD X3",
+		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_PCM | LINE6_CAP_HWMON | LINE6_CAP_IN_NEEDS_OUT,
+		.altsetting = 1,
+		.ep_ctrl_r = 0x81,
+		.ep_ctrl_w = 0x01,
+		.ep_audio_r = 0x86,
+		.ep_audio_w = 0x02,
+	},
 };
 
 /*
@@ -169,7 +419,7 @@ static int podhd_probe(struct usb_interface *interface,
 {
 	return line6_probe(interface, id, "Line6-PODHD",
 			   &podhd_properties_table[id->driver_info],
-			   podhd_init, sizeof(struct usb_line6));
+			   podhd_init, sizeof(struct usb_line6_podhd));
 }
 
 static struct usb_driver podhd_driver = {
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v4 09/12] ALSA: line6: Add support for POD X3 Live (only USB ID differs from POD X3)
  2016-09-18 18:59 ` [PATCH v4 00/12] Line6 POD X3/X3Live suport Andrej Krutak
                     ` (7 preceding siblings ...)
  2016-09-18 18:59   ` [PATCH v4 08/12] ALSA: line6: Add support for POD X3 Andrej Krutak
@ 2016-09-18 18:59   ` Andrej Krutak
  2016-09-18 18:59   ` [PATCH v4 10/12] ALSA: line6: Only determine control port properties if needed Andrej Krutak
                     ` (3 subsequent siblings)
  12 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-09-18 18:59 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/podhd.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c
index 8cf0a98..8246ea5 100644
--- a/sound/usb/line6/podhd.c
+++ b/sound/usb/line6/podhd.c
@@ -37,6 +37,7 @@ enum {
 	LINE6_PODHD500_0,
 	LINE6_PODHD500_1,
 	LINE6_PODX3,
+	LINE6_PODX3LIVE
 };
 
 struct usb_line6_podhd {
@@ -348,6 +349,7 @@ static const struct usb_device_id podhd_id_table[] = {
 	{ LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500_0 },
 	{ LINE6_IF_NUM(0x414D, 1), .driver_info = LINE6_PODHD500_1 },
 	{ LINE6_IF_NUM(0x414A, 0), .driver_info = LINE6_PODX3 },
+	{ LINE6_IF_NUM(0x414B, 0), .driver_info = LINE6_PODX3LIVE },
 	{}
 };
 
@@ -409,6 +411,17 @@ static const struct line6_properties podhd_properties_table[] = {
 		.ep_audio_r = 0x86,
 		.ep_audio_w = 0x02,
 	},
+	[LINE6_PODX3LIVE] = {
+		.id = "PODX3LIVE",
+		.name = "POD X3 LIVE",
+		.capabilities	= LINE6_CAP_CONTROL
+				| LINE6_CAP_PCM | LINE6_CAP_HWMON | LINE6_CAP_IN_NEEDS_OUT,
+		.altsetting = 1,
+		.ep_ctrl_r = 0x81,
+		.ep_ctrl_w = 0x01,
+		.ep_audio_r = 0x86,
+		.ep_audio_w = 0x02,
+	},
 };
 
 /*
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v4 10/12] ALSA: line6: Only determine control port properties if needed
  2016-09-18 18:59 ` [PATCH v4 00/12] Line6 POD X3/X3Live suport Andrej Krutak
                     ` (8 preceding siblings ...)
  2016-09-18 18:59   ` [PATCH v4 09/12] ALSA: line6: Add support for POD X3 Live (only USB ID differs from POD X3) Andrej Krutak
@ 2016-09-18 18:59   ` Andrej Krutak
  2016-09-18 18:59   ` [PATCH v4 11/12] ALSA: line6: Cleanup podhd initialization Andrej Krutak
                     ` (2 subsequent siblings)
  12 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-09-18 18:59 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

Not all line6 devices use the control port.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/driver.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 853a143..8a71d45 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -612,9 +612,8 @@ int line6_probe(struct usb_interface *interface,
 		goto error;
 	}
 
-	line6_get_interval(line6);
-
 	if (properties->capabilities & LINE6_CAP_CONTROL) {
+		line6_get_interval(line6);
 		ret = line6_init_cap_control(line6);
 		if (ret < 0)
 			goto error;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v4 11/12] ALSA: line6: Cleanup podhd initialization
  2016-09-18 18:59 ` [PATCH v4 00/12] Line6 POD X3/X3Live suport Andrej Krutak
                     ` (9 preceding siblings ...)
  2016-09-18 18:59   ` [PATCH v4 10/12] ALSA: line6: Only determine control port properties if needed Andrej Krutak
@ 2016-09-18 18:59   ` Andrej Krutak
  2016-09-18 18:59   ` [PATCH v4 12/12] ALSA: line6: Add hwdep interface to access the POD control messages Andrej Krutak
  2016-09-19 21:07   ` [PATCH v4 00/12] Line6 POD X3/X3Live suport Takashi Iwai
  12 siblings, 0 replies; 81+ messages in thread
From: Andrej Krutak @ 2016-09-18 18:59 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

Only initialize PCM for POD HD devices that support it.
No POD HD seems to support MIDI, thus drop the initialization.

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 sound/usb/line6/podhd.c | 19 ++++++++-----------
 1 file changed, 8 insertions(+), 11 deletions(-)

diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c
index 8246ea5..193eb29 100644
--- a/sound/usb/line6/podhd.c
+++ b/sound/usb/line6/podhd.c
@@ -314,17 +314,14 @@ static int podhd_init(struct usb_line6 *line6,
 			return err;
 	}
 
-	/* initialize MIDI subsystem: */
-	err = line6_init_midi(line6);
-	if (err < 0)
-		return err;
-
-	/* initialize PCM subsystem: */
-	err = line6_init_pcm(line6,
-		(id->driver_info == LINE6_PODX3) ? &podx3_pcm_properties :
-		&podhd_pcm_properties);
-	if (err < 0)
-		return err;
+	if (pod->line6.properties->capabilities & LINE6_CAP_PCM) {
+		/* initialize PCM subsystem: */
+		err = line6_init_pcm(line6,
+			(id->driver_info == LINE6_PODX3) ? &podx3_pcm_properties :
+			&podhd_pcm_properties);
+		if (err < 0)
+			return err;
+	}
 
 	if (!(pod->line6.properties->capabilities & LINE6_CAP_CONTROL)) {
 		/* register USB audio system directly */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* [PATCH v4 12/12] ALSA: line6: Add hwdep interface to access the POD control messages
  2016-09-18 18:59 ` [PATCH v4 00/12] Line6 POD X3/X3Live suport Andrej Krutak
                     ` (10 preceding siblings ...)
  2016-09-18 18:59   ` [PATCH v4 11/12] ALSA: line6: Cleanup podhd initialization Andrej Krutak
@ 2016-09-18 18:59   ` Andrej Krutak
  2016-09-19 21:06     ` Takashi Iwai
  2016-09-19 21:07   ` [PATCH v4 00/12] Line6 POD X3/X3Live suport Takashi Iwai
  12 siblings, 1 reply; 81+ messages in thread
From: Andrej Krutak @ 2016-09-18 18:59 UTC (permalink / raw)
  To: tiwai, perex, stefanha, grabner, alsa-devel; +Cc: Andrej Krutak

We must do it this way, because e.g. POD X3 won't play any sound unless
the host listens on the bulk EP, so we cannot export it only via libusb.

The driver currently doesn't use the bulk EP messages in other way,
in future it could e.g. sense/modify volume(s).

Signed-off-by: Andrej Krutak <dev@andree.sk>
---
 include/uapi/sound/asound.h |   3 +-
 sound/usb/line6/driver.c    | 151 ++++++++++++++++++++++++++++++++++++++++++--
 sound/usb/line6/driver.h    |  23 ++++++-
 3 files changed, 170 insertions(+), 7 deletions(-)

diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index 609cadb..be353a7 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -106,9 +106,10 @@ enum {
 	SNDRV_HWDEP_IFACE_FW_OXFW,	/* Oxford OXFW970/971 based device */
 	SNDRV_HWDEP_IFACE_FW_DIGI00X,	/* Digidesign Digi 002/003 family */
 	SNDRV_HWDEP_IFACE_FW_TASCAM,	/* TASCAM FireWire series */
+	SNDRV_HWDEP_IFACE_LINE6,	/* Line6 USB processors */
 
 	/* Don't forget to change the following: */
-	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_TASCAM
+	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_LINE6
 };
 
 struct snd_hwdep_info {
diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 8a71d45..3536efe 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -17,6 +17,7 @@
 
 #include <sound/core.h>
 #include <sound/initval.h>
+#include <sound/hwdep.h>
 
 #include "capture.h"
 #include "driver.h"
@@ -303,7 +304,7 @@ static void line6_data_received(struct urb *urb)
 		for (;;) {
 			done =
 				line6_midibuf_read(mb, line6->buffer_message,
-						LINE6_MESSAGE_MAXLEN);
+						LINE6_MIDI_MESSAGE_MAXLEN);
 
 			if (done == 0)
 				break;
@@ -315,8 +316,11 @@ static void line6_data_received(struct urb *urb)
 				line6->process_message(line6);
 		}
 	} else {
+		line6->buffer_message = urb->transfer_buffer;
+		line6->message_length = urb->actual_length;
 		if (line6->process_message)
 			line6->process_message(line6);
+		line6->buffer_message = NULL;
 	}
 
 	line6_start_listen(line6);
@@ -473,14 +477,16 @@ static void line6_destruct(struct snd_card *card)
 	struct usb_line6 *line6 = card->private_data;
 	struct usb_device *usbdev = line6->usbdev;
 
-	/* free buffer memory first: */
-	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)
+	/* Free buffer memory first. We cannot depend on the existence of private
+	 * data from the (podhd) module, it may be gone already during this call */
+	if (line6->buffer_message)
 		kfree(line6->buffer_message);
 
 	kfree(line6->buffer_listen);
 
 	/* then free URBs: */
 	usb_free_urb(line6->urb_listen);
+	line6->urb_listen = NULL;
 
 	/* decrement reference counters: */
 	usb_put_dev(usbdev);
@@ -522,6 +528,138 @@ static void line6_get_interval(struct usb_line6 *line6)
 	}
 }
 
+
+/* Enable buffering of incoming messages, flush the buffer */
+static int line6_hwdep_open(struct snd_hwdep *hw, struct file *file)
+{
+	struct usb_line6 *line6 = hw->private_data;
+
+	/* NOTE: hwdep layer provides atomicity here */
+
+	line6->messages.active = 1;
+
+	return 0;
+}
+
+/* Stop buffering */
+static int line6_hwdep_release(struct snd_hwdep *hw, struct file *file)
+{
+	struct usb_line6 *line6 = hw->private_data;
+
+	line6->messages.active = 0;
+
+	return 0;
+}
+
+/* Read from circular buffer, return to user */
+static long
+line6_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+					loff_t *offset)
+{
+	struct usb_line6 *line6 = hwdep->private_data;
+	long rv = 0;
+	unsigned int out_count;
+
+	if (mutex_lock_interruptible(&line6->messages.read_lock))
+		return -ERESTARTSYS;
+
+	while (kfifo_len(&line6->messages.fifo) == 0) {
+		mutex_unlock(&line6->messages.read_lock);
+
+		rv = wait_event_interruptible(
+			line6->messages.wait_queue,
+			kfifo_len(&line6->messages.fifo) != 0);
+		if (rv < 0)
+			return rv;
+
+		if (mutex_lock_interruptible(&line6->messages.read_lock))
+			return -ERESTARTSYS;
+	}
+
+	if (kfifo_peek_len(&line6->messages.fifo) > count) {
+		/* Buffer too small; allow re-read of the current item... */
+		rv = -EINVAL;
+	} else {
+		rv = kfifo_to_user(&line6->messages.fifo, buf, count, &out_count);
+		if (rv == 0)
+			rv = out_count;
+	}
+
+	mutex_unlock(&line6->messages.read_lock);
+	return rv;
+}
+
+/* Write directly (no buffering) to device by user*/
+static long
+line6_hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count,
+					loff_t *offset)
+{
+	struct usb_line6 *line6 = hwdep->private_data;
+	int rv;
+	char *data_copy;
+
+	if (count > line6->max_packet_size * LINE6_RAW_MESSAGES_MAXCOUNT) {
+		/* This is an arbitrary limit - still better than nothing... */
+		return -EINVAL;
+	}
+
+	data_copy = memdup_user(data, count);
+	if (IS_ERR(ERR_PTR))
+		return -ENOMEM;
+
+	rv = line6_send_raw_message(line6, data_copy, count);
+
+	kfree(data_copy);
+	return rv;
+}
+
+static const struct snd_hwdep_ops hwdep_ops = {
+	.open    = line6_hwdep_open,
+	.release = line6_hwdep_release,
+	.read    = line6_hwdep_read,
+	.write   = line6_hwdep_write,
+};
+
+/* Insert into circular buffer */
+static void line6_hwdep_push_message(struct usb_line6 *line6)
+{
+	if (!line6->messages.active)
+		return;
+
+	if (kfifo_avail(&line6->messages.fifo) >= line6->message_length) {
+		/* No race condition here, there's only one writer */
+		kfifo_in(&line6->messages.fifo,
+			line6->buffer_message, line6->message_length);
+	} /* else TODO: signal overflow */
+
+	wake_up_interruptible(&line6->messages.wait_queue);
+}
+
+static int line6_hwdep_init(struct usb_line6 *line6)
+{
+	int err;
+	struct snd_hwdep *hwdep;
+
+	/* TODO: usb_driver_claim_interface(); */
+	line6->process_message = line6_hwdep_push_message;
+	line6->messages.active = 0;
+	init_waitqueue_head(&line6->messages.wait_queue);
+	mutex_init(&line6->messages.read_lock);
+	INIT_KFIFO(line6->messages.fifo);
+
+	err = snd_hwdep_new(line6->card, "config", 0, &hwdep);
+	if (err < 0)
+		goto end;
+	strcpy(hwdep->name, "config");
+	hwdep->iface = SNDRV_HWDEP_IFACE_LINE6;
+	hwdep->ops = hwdep_ops;
+	hwdep->private_data = line6;
+	hwdep->exclusive = true;
+
+end:
+	return err;
+}
+
 static int line6_init_cap_control(struct usb_line6 *line6)
 {
 	int ret;
@@ -536,9 +674,13 @@ static int line6_init_cap_control(struct usb_line6 *line6)
 		return -ENOMEM;
 
 	if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
-		line6->buffer_message = kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL);
+		line6->buffer_message = kmalloc(LINE6_MIDI_MESSAGE_MAXLEN, GFP_KERNEL);
 		if (!line6->buffer_message)
 			return -ENOMEM;
+	} else {
+		ret = line6_hwdep_init(line6);
+		if (ret < 0)
+			return ret;
 	}
 
 	ret = line6_start_listen(line6);
@@ -716,3 +858,4 @@ EXPORT_SYMBOL_GPL(line6_resume);
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
+
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index d7eefe1..cf9f077 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -12,8 +12,9 @@
 #ifndef DRIVER_H
 #define DRIVER_H
 
-#include <linux/spinlock.h>
 #include <linux/usb.h>
+#include <linux/mutex.h>
+#include <linux/kfifo.h>
 #include <sound/core.h>
 
 #include "midi.h"
@@ -32,7 +33,16 @@
 
 #define LINE6_TIMEOUT 1
 #define LINE6_BUFSIZE_LISTEN 64
-#define LINE6_MESSAGE_MAXLEN 256
+#define LINE6_MIDI_MESSAGE_MAXLEN 256
+
+#define LINE6_RAW_MESSAGES_MAXCOUNT_ORDER 7
+/* 4k packets are common, BUFSIZE * MAXCOUNT should be bigger... */
+#define LINE6_RAW_MESSAGES_MAXCOUNT (1 << LINE6_RAW_MESSAGES_MAXCOUNT_ORDER)
+
+
+#if LINE6_BUFSIZE_LISTEN > 65535
+#error "Use dynamic fifo instead"
+#endif
 
 /*
 	Line 6 MIDI control commands
@@ -156,6 +166,15 @@ struct usb_line6 {
 	/* Length of message to be processed, generated from MIDI layer  */
 	int message_length;
 
+	/* Circular buffer for non-MIDI control messages */
+	struct {
+		struct mutex read_lock;
+		wait_queue_head_t wait_queue;
+		int active:1;
+		STRUCT_KFIFO_REC_2(LINE6_BUFSIZE_LISTEN * LINE6_RAW_MESSAGES_MAXCOUNT)
+			fifo;
+	} messages;
+
 	/* If MIDI is supported, buffer_message contains the pre-processed data;
 	 * otherwise the data is only in urb_listen (buffer_incoming). */
 	void (*process_message)(struct usb_line6 *);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 81+ messages in thread

* Re: [PATCH v4 12/12] ALSA: line6: Add hwdep interface to access the POD control messages
  2016-09-18 18:59   ` [PATCH v4 12/12] ALSA: line6: Add hwdep interface to access the POD control messages Andrej Krutak
@ 2016-09-19 21:06     ` Takashi Iwai
  0 siblings, 0 replies; 81+ messages in thread
From: Takashi Iwai @ 2016-09-19 21:06 UTC (permalink / raw)
  To: Andrej Krutak; +Cc: stefanha, alsa-devel, grabner

On Sun, 18 Sep 2016 20:59:32 +0200,
Andrej Krutak wrote:
> @@ -156,6 +166,15 @@ struct usb_line6 {
>  	/* Length of message to be processed, generated from MIDI layer  */
>  	int message_length;
>  
> +	/* Circular buffer for non-MIDI control messages */
> +	struct {
> +		struct mutex read_lock;
> +		wait_queue_head_t wait_queue;
> +		int active:1;

This should be unsigned int.  Otherwise 1 bit int is handled as -1.


Takashi

^ permalink raw reply	[flat|nested] 81+ messages in thread

* Re: [PATCH v4 00/12] Line6 POD X3/X3Live suport
  2016-09-18 18:59 ` [PATCH v4 00/12] Line6 POD X3/X3Live suport Andrej Krutak
                     ` (11 preceding siblings ...)
  2016-09-18 18:59   ` [PATCH v4 12/12] ALSA: line6: Add hwdep interface to access the POD control messages Andrej Krutak
@ 2016-09-19 21:07   ` Takashi Iwai
  12 siblings, 0 replies; 81+ messages in thread
From: Takashi Iwai @ 2016-09-19 21:07 UTC (permalink / raw)
  To: Andrej Krutak; +Cc: stefanha, alsa-devel, grabner

On Sun, 18 Sep 2016 20:59:20 +0200,
Andrej Krutak wrote:
> 
> Hello all,
> 
> 
> attached are the yet again reworked patches. Basically the diff is only some
> formatting. In addition, some of the previously big patches were split to
> multiple smaller ones. Compared to v3, there is a small bisectability/patch
> sanity fix.
> 
> I have checked that after each one, the compilation works, but not that the
> result doesn't segfault or even work - I don't have the hardware previously
> supported by the driver and the most interesting changes are only useful as
> a whole for POD X3.
> 
> Greetings!
> 
> 
> Andrej Krutak (12):
>   ALSA: line6: Enable different number of URBs for frame transfers
>   ALSA: line6: Add high-speed USB support
>   ALSA: line6: Support assymetrical in/out configurations
>   ALSA: line6: Allow different channel numbers for in/out
>   ALSA: line6: Add LINE6_CAP_IN_NEEDS_OUT, a void playback stream during
>     capture
>   ALSA: line6: Distinguish device init (ctrl EP) and MIDI data transfer
>     (int EP)
>   ALSA: line6: Allow processing of raw incoming messages
>   ALSA: line6: Add support for POD X3
>   ALSA: line6: Add support for POD X3 Live (only USB ID differs from POD
>     X3)
>   ALSA: line6: Only determine control port properties if needed
>   ALSA: line6: Cleanup podhd initialization
>   ALSA: line6: Add hwdep interface to access the POD control messages

OK, now all patches look almost good, so I applied them with a few
manual fixes of coding style issues checkpatch.pl copmlained, as well
as a fix of bit field type in patch 12.

Thanks!


Takashi

^ permalink raw reply	[flat|nested] 81+ messages in thread

end of thread, other threads:[~2016-09-19 21:07 UTC | newest]

Thread overview: 81+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-08-11 19:02 [PATCH 00/15] Line6 POD X3/X3Live suport Andrej Krutak
2016-08-11 19:02 ` [PATCH 01/15] ALSA: line6: Make driver configuration more generic Andrej Krutak
2016-08-12  8:24   ` Takashi Iwai
2016-08-11 19:02 ` [PATCH 02/15] ALSA: line6: Add LINE6_CAP_IN_NEEDS_OUT, a void playback stream during capture Andrej Krutak
2016-08-11 19:02 ` [PATCH 03/15] ALSA: line6: Distinguish device init (ctrl EP) and MIDI data transfer (int EP) Andrej Krutak
2016-08-11 19:02 ` [PATCH 04/15] ALSA: line6: Add support for POD X3 Andrej Krutak
2016-08-11 19:02 ` [PATCH 05/15] ALSA: line6: Use device_create_file instead of snd_card_add_dev_attr Andrej Krutak
2016-08-12  8:26   ` Takashi Iwai
2016-08-11 19:02 ` [PATCH 06/15] ALSA: line6: Allow bulk endpoints instead of interrupt endpoints Andrej Krutak
2016-08-11 19:02 ` [PATCH 07/15] ALSA: line6: Allow processing of raw incoming messages Andrej Krutak
2016-08-11 19:02 ` [PATCH 08/15] ALSA: line6: Cleanup initialization Andrej Krutak
2016-08-11 19:02 ` [PATCH 09/15] ALSA: line6: Add hwdep interface to access the POD control messages Andrej Krutak
2016-08-12  8:44   ` Takashi Iwai
2016-08-12 11:10     ` Andrej Kruták
2016-08-12 12:03       ` Takashi Iwai
2016-08-12 12:15         ` Andrej Kruták
2016-08-12 12:30           ` Takashi Iwai
2016-08-12 16:43             ` Andrej Kruták
2016-08-12 20:01               ` Takashi Iwai
2016-08-14  7:59                 ` Andrej Kruták
2016-08-11 19:02 ` [PATCH 10/15] ALSA: line6: Add proper locks for hwdep open/release/read Andrej Krutak
2016-08-12  8:44   ` Takashi Iwai
2016-08-11 19:02 ` [PATCH 11/15] ALSA: line6: Only free buffer if it is set Andrej Krutak
2016-08-12  8:45   ` Takashi Iwai
2016-08-11 19:02 ` [PATCH 12/15] ALSA: line6: Give up on the lock while URBs are released Andrej Krutak
2016-08-12  8:45   ` Takashi Iwai
2016-08-12 11:14     ` Andrej Kruták
2016-08-12 12:04       ` Takashi Iwai
2016-08-11 19:02 ` [PATCH 13/15] ALSA: line6: Add support for POD X3 Live (only USB ID differs from POD X3) Andrej Krutak
2016-08-11 19:02 ` [PATCH 14/15] ALSA: line6: Give up hwdep spinlock temporarily during read operation Andrej Krutak
2016-08-12  8:46   ` Takashi Iwai
2016-08-11 19:02 ` [PATCH 15/15] ALSA: line6: Remove double line6_pcm_release() after failed acquire Andrej Krutak
2016-08-12  8:46   ` Takashi Iwai
2016-08-12  8:14 ` [PATCH 00/15] Line6 POD X3/X3Live suport Takashi Iwai
2016-08-12 10:31   ` Andrej Kruták
2016-08-18 22:20 ` [PATCH v2 0/9] " Andrej Krutak
2016-08-18 22:20   ` [PATCH v2 1/9] ALSA: line6: Make driver configuration more generic Andrej Krutak
2016-08-24 14:50     ` Takashi Iwai
2016-08-18 22:20   ` [PATCH v2 2/9] ALSA: line6: Add LINE6_CAP_IN_NEEDS_OUT, a void playback stream during capture Andrej Krutak
2016-08-24 14:53     ` Takashi Iwai
2016-08-18 22:20   ` [PATCH v2 3/9] ALSA: line6: Distinguish device init (ctrl EP) and MIDI data transfer (int EP) Andrej Krutak
2016-08-24 14:56     ` Takashi Iwai
2016-08-18 22:20   ` [PATCH v2 4/9] ALSA: line6: Add support for POD X3 Andrej Krutak
2016-08-18 22:20   ` [PATCH v2 5/9] ALSA: line6: Add support for POD X3 Live (only USB ID differs from POD X3) Andrej Krutak
2016-08-18 22:20   ` [PATCH v2 6/9] ALSA: line6: Allow bulk endpoints instead of interrupt endpoints Andrej Krutak
2016-08-24 15:02     ` Takashi Iwai
2016-08-18 22:20   ` [PATCH v2 7/9] ALSA: line6: Allow processing of raw incoming messages Andrej Krutak
2016-08-24 15:05     ` Takashi Iwai
2016-08-30 14:35       ` Andrej Kruták
2016-08-18 22:20   ` [PATCH v2 8/9] ALSA: line6: Cleanup initialization Andrej Krutak
2016-08-24 15:06     ` Takashi Iwai
2016-08-18 22:20   ` [PATCH v2 9/9] ALSA: line6: Add hwdep interface to access the POD control messages Andrej Krutak
2016-09-16  9:12 ` [PATCH v3 00/12] Line6 POD X3/X3Live suport Andrej Krutak
2016-09-16  9:12   ` [PATCH v3 01/12] ALSA: line6: Enable different number of URBs for frame transfers Andrej Krutak
2016-09-16  9:12   ` [PATCH v3 02/12] ALSA: line6: Add high-speed USB support Andrej Krutak
2016-09-16  9:12   ` [PATCH v3 03/12] ALSA: line6: Support assymetrical in/out configurations Andrej Krutak
2016-09-16 18:34     ` kbuild test robot
2016-09-16  9:12   ` [PATCH v3 04/12] ALSA: line6: Allow different channel numbers for in/out Andrej Krutak
2016-09-16  9:12   ` [PATCH v3 05/12] ALSA: line6: Add LINE6_CAP_IN_NEEDS_OUT, a void playback stream during capture Andrej Krutak
2016-09-16  9:12   ` [PATCH v3 06/12] ALSA: line6: Distinguish device init (ctrl EP) and MIDI data transfer (int EP) Andrej Krutak
2016-09-16  9:12   ` [PATCH v3 07/12] ALSA: line6: Allow processing of raw incoming messages Andrej Krutak
2016-09-16  9:12   ` [PATCH v3 08/12] ALSA: line6: Add support for POD X3 Andrej Krutak
2016-09-16  9:12   ` [PATCH v3 09/12] ALSA: line6: Add support for POD X3 Live (only USB ID differs from POD X3) Andrej Krutak
2016-09-16  9:12   ` [PATCH v3 10/12] ALSA: line6: Only determine control port properties if needed Andrej Krutak
2016-09-16  9:12   ` [PATCH v3 11/12] ALSA: line6: Cleanup podhd initialization Andrej Krutak
2016-09-16  9:12   ` [PATCH v3 12/12] ALSA: line6: Add hwdep interface to access the POD control messages Andrej Krutak
2016-09-18 18:59 ` [PATCH v4 00/12] Line6 POD X3/X3Live suport Andrej Krutak
2016-09-18 18:59   ` [PATCH v4 01/12] ALSA: line6: Enable different number of URBs for frame transfers Andrej Krutak
2016-09-18 18:59   ` [PATCH v4 02/12] ALSA: line6: Add high-speed USB support Andrej Krutak
2016-09-18 18:59   ` [PATCH v4 03/12] ALSA: line6: Support assymetrical in/out configurations Andrej Krutak
2016-09-18 18:59   ` [PATCH v4 04/12] ALSA: line6: Allow different channel numbers for in/out Andrej Krutak
2016-09-18 18:59   ` [PATCH v4 05/12] ALSA: line6: Add LINE6_CAP_IN_NEEDS_OUT, a void playback stream during capture Andrej Krutak
2016-09-18 18:59   ` [PATCH v4 06/12] ALSA: line6: Distinguish device init (ctrl EP) and MIDI data transfer (int EP) Andrej Krutak
2016-09-18 18:59   ` [PATCH v4 07/12] ALSA: line6: Allow processing of raw incoming messages Andrej Krutak
2016-09-18 18:59   ` [PATCH v4 08/12] ALSA: line6: Add support for POD X3 Andrej Krutak
2016-09-18 18:59   ` [PATCH v4 09/12] ALSA: line6: Add support for POD X3 Live (only USB ID differs from POD X3) Andrej Krutak
2016-09-18 18:59   ` [PATCH v4 10/12] ALSA: line6: Only determine control port properties if needed Andrej Krutak
2016-09-18 18:59   ` [PATCH v4 11/12] ALSA: line6: Cleanup podhd initialization Andrej Krutak
2016-09-18 18:59   ` [PATCH v4 12/12] ALSA: line6: Add hwdep interface to access the POD control messages Andrej Krutak
2016-09-19 21:06     ` Takashi Iwai
2016-09-19 21:07   ` [PATCH v4 00/12] Line6 POD X3/X3Live suport Takashi Iwai

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.