All of lore.kernel.org
 help / color / mirror / Atom feed
* Fwd: [PATCH] ehci-hcd: complete iso urbs ASAP for number_of_packets != (n * 8) also
@ 2007-11-07 15:41 Karsten Wiese
  2007-11-07 18:55 ` [PATCH] US-122L Driver, v0 Karsten Wiese
  0 siblings, 1 reply; 7+ messages in thread
From: Karsten Wiese @ 2007-11-07 15:41 UTC (permalink / raw)
  To: alsa-devel

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

Hi

I just sent attached patch to usb-devel.
It makes special workaround for iso usb communication using ehci-hcd highspeed
needless.
Comments, Bug/success reports welcome!

kind regards
      Karsten

Upstream (as of 2.6.23) ehci-hcd only completes iso urbs, if the last frame
(= 8 uframes) they overlapped with has elapsed. That can be as late as when
the following urb emits its interrupt on completion.
soundcard drivers tend to work around by only transfering iso urbs with
number_of_packets = (n * 8).
Patch lets iso urbs complete asap by scanning itd always up to the elapsed
uframe. An itd's last scanned uframe is stored in the new struct ehci_itd
member uframe_scanned.
Itds stay kept in the hcd's schedule until the complete frame they cover
has elapsed, like without patch.
To make this possible, ehci->periodic_sched is changed based on the number of
active itds instead of the number of active iso urbs.

Signed-off-by: Karsten Wiese <fzu@wemgehoertderstaat.de>
---
 drivers/usb/host/ehci-sched.c |  108 ++++++++++++++++++++---------------------
 drivers/usb/host/ehci.h       |    1 +
 2 files changed, 54 insertions(+), 55 deletions(-)

diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index e682f23..c456b16 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -1481,6 +1481,8 @@ itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd)
 	itd->frame = frame;
 	wmb ();
 	ehci->periodic[frame] = cpu_to_hc32(ehci, itd->itd_dma | Q_TYPE_ITD);
+	if (unlikely (!ehci->periodic_sched++))
+		enable_periodic (ehci);
 }
 
 /* fit urb's itds into the selected schedule slot; activate as needed */
@@ -1553,30 +1555,30 @@ itd_link_urb (
 	urb->hcpriv = NULL;
 
 	timer_action (ehci, TIMER_IO_WATCHDOG);
-	if (unlikely (!ehci->periodic_sched++))
-		return enable_periodic (ehci);
 	return 0;
 }
 
 #define	ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR)
 
-static unsigned
-itd_complete (
-	struct ehci_hcd	*ehci,
-	struct ehci_itd	*itd
-) {
+static bool
+itd_scan (
+	struct ehci_hcd *ehci,
+	struct ehci_itd	*itd,
+	unsigned uframe_after
+)
+{
 	struct urb				*urb = itd->urb;
 	struct usb_iso_packet_descriptor	*desc;
 	u32					t;
 	unsigned				uframe;
 	int					urb_index = -1;
 	struct ehci_iso_stream			*stream = itd->stream;
-	struct usb_device			*dev;
 
 	/* for each uframe with a packet */
-	for (uframe = 0; uframe < 8; uframe++) {
+	for (uframe = itd->uframe_scanned; uframe < uframe_after; uframe++) {
 		if (likely (itd->index[uframe] == -1))
 			continue;
+
 		urb_index = itd->index[uframe];
 		desc = &urb->iso_frame_desc [urb_index];
 
@@ -1603,45 +1605,53 @@ itd_complete (
 			desc->status = 0;
 			desc->actual_length = EHCI_ITD_LENGTH (t);
 		}
+
+		/* handle completion now? */
+		if (urb_index + 1 == urb->number_of_packets) {
+			struct usb_device *dev;
+
+			usb_put_urb (urb);
+			/* ASSERT: it's really the last itd for this urb
+			   struct ehci_itd	*stritd;
+			   list_for_each_entry (stritd, &stream->td_list, itd_list)
+			   BUG_ON (stritd != itd && stritd->urb == urb);
+			*/
+			itd->urb = NULL;
+
+			/* give urb back to the driver ... can be out-of-order */
+			dev = urb->dev;
+			ehci_urb_done (ehci, urb);
+			urb = NULL;
+
+			ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
+
+			if (unlikely (list_empty (&stream->td_list))) {
+				ehci_to_hcd(ehci)->self.bandwidth_allocated
+					-= stream->bandwidth;
+				ehci_vdbg (ehci,
+					   "deschedule devp %s ep%d%s-iso\n",
+					   dev->devpath, stream->bEndpointAddress & 0x0f,
+					   (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
+			}
+			iso_stream_put (ehci, stream);
+		}
 	}
 
-	usb_put_urb (urb);
-	itd->urb = NULL;
+	itd->uframe_scanned = uframe_after;
+	if (uframe_after < 8)
+		return false;
+
 	itd->stream = NULL;
+	itd->uframe_scanned = 0;
 	list_move (&itd->itd_list, &stream->free_list);
 	iso_stream_put (ehci, stream);
 
-	/* handle completion now? */
-	if (likely ((urb_index + 1) != urb->number_of_packets))
-		return 0;
-
-	/* ASSERT: it's really the last itd for this urb
-	list_for_each_entry (itd, &stream->td_list, itd_list)
-		BUG_ON (itd->urb == urb);
-	 */
-
-	/* give urb back to the driver ... can be out-of-order */
-	dev = urb->dev;
-	ehci_urb_done (ehci, urb);
-	urb = NULL;
-
 	/* defer stopping schedule; completion can submit */
 	ehci->periodic_sched--;
 	if (unlikely (!ehci->periodic_sched))
 		(void) disable_periodic (ehci);
-	ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
 
-	if (unlikely (list_empty (&stream->td_list))) {
-		ehci_to_hcd(ehci)->self.bandwidth_allocated
-				-= stream->bandwidth;
-		ehci_vdbg (ehci,
-			"deschedule devp %s ep%d%s-iso\n",
-			dev->devpath, stream->bEndpointAddress & 0x0f,
-			(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
-	}
-	iso_stream_put (ehci, stream);
-
-	return 1;
+	return true;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -2156,30 +2166,18 @@ restart:
 				q = q.fstn->fstn_next;
 				break;
 			case Q_TYPE_ITD:
-				/* skip itds for later in the frame */
 				rmb ();
-				for (uf = live ? uframes : 8; uf < 8; uf++) {
-					if (0 == (q.itd->hw_transaction [uf]
-							& ITD_ACTIVE(ehci)))
-						continue;
+				modified = itd_scan(ehci, q.itd, uf = live ? uframes : 8);
+				if (!modified) {
 					q_p = &q.itd->itd_next;
 					hw_p = &q.itd->hw_next;
-					type = Q_NEXT_TYPE(ehci,
-							q.itd->hw_next);
-					q = *q_p;
-					break;
+				} else {
+					*q_p = q.itd->itd_next;
+					*hw_p = q.itd->hw_next;
 				}
-				if (uf != 8)
-					break;
-
-				/* this one's ready ... HC won't cache the
-				 * pointer for much longer, if at all.
-				 */
-				*q_p = q.itd->itd_next;
-				*hw_p = q.itd->hw_next;
 				type = Q_NEXT_TYPE(ehci, q.itd->hw_next);
+
 				wmb();
-				modified = itd_complete (ehci, q.itd);
 				q = *q_p;
 				break;
 			case Q_TYPE_SITD:
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 951d69f..df35964 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -581,6 +581,7 @@ struct ehci_itd {
 	struct urb		*urb;
 	struct ehci_iso_stream	*stream;	/* endpoint's queue */
 	struct list_head	itd_list;	/* list of stream's itds */
+	unsigned		uframe_scanned;
 
 	/* any/all hw_transactions here may be used by that urb */
 	unsigned		frame;		/* where scheduled */
-- 
1.5.3.3


[-- Attachment #2: Karsten Wiese <fzu@wemgehoertderstaat.de>: [PATCH] ehci-hcd: complete iso urbs ASAP for number_of_packets != (n * 8) also --]
[-- Type: message/rfc822, Size: 6637 bytes --]

From: Karsten Wiese <fzu@wemgehoertderstaat.de>
To: linux-usb-devel@lists.sourceforge.net
Subject: [PATCH] ehci-hcd: complete iso urbs ASAP for number_of_packets != (n * 8) also
Date: Wed, 7 Nov 2007 16:16:34 +0100
Message-ID: <200711071616.34194.fzu@wemgehoertderstaat.de>


Upstream (as of 2.6.23) ehci-hcd only completes iso urbs, if the last frame
(= 8 uframes) they overlapped with has elapsed. That can be as late as when
the following urb emits its interrupt on completion.
soundcard drivers tend to work around by only transfering iso urbs with
number_of_packets = (n * 8).
Patch lets iso urbs complete asap by scanning itd always up to the elapsed
uframe. An itd's last scanned uframe is stored in the new struct ehci_itd
member uframe_scanned.
Itds stay kept in the hcd's schedule until the complete frame they cover
has elapsed, like without patch.
To make this possible, ehci->periodic_sched is changed based on the number of
active itds instead of the number of active iso urbs.

Signed-off-by: Karsten Wiese <fzu@wemgehoertderstaat.de>
---
 drivers/usb/host/ehci-sched.c |  108 ++++++++++++++++++++---------------------
 drivers/usb/host/ehci.h       |    1 +
 2 files changed, 54 insertions(+), 55 deletions(-)

diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index e682f23..c456b16 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -1481,6 +1481,8 @@ itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd)
 	itd->frame = frame;
 	wmb ();
 	ehci->periodic[frame] = cpu_to_hc32(ehci, itd->itd_dma | Q_TYPE_ITD);
+	if (unlikely (!ehci->periodic_sched++))
+		enable_periodic (ehci);
 }
 
 /* fit urb's itds into the selected schedule slot; activate as needed */
@@ -1553,30 +1555,30 @@ itd_link_urb (
 	urb->hcpriv = NULL;
 
 	timer_action (ehci, TIMER_IO_WATCHDOG);
-	if (unlikely (!ehci->periodic_sched++))
-		return enable_periodic (ehci);
 	return 0;
 }
 
 #define	ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR)
 
-static unsigned
-itd_complete (
-	struct ehci_hcd	*ehci,
-	struct ehci_itd	*itd
-) {
+static bool
+itd_scan (
+	struct ehci_hcd *ehci,
+	struct ehci_itd	*itd,
+	unsigned uframe_after
+)
+{
 	struct urb				*urb = itd->urb;
 	struct usb_iso_packet_descriptor	*desc;
 	u32					t;
 	unsigned				uframe;
 	int					urb_index = -1;
 	struct ehci_iso_stream			*stream = itd->stream;
-	struct usb_device			*dev;
 
 	/* for each uframe with a packet */
-	for (uframe = 0; uframe < 8; uframe++) {
+	for (uframe = itd->uframe_scanned; uframe < uframe_after; uframe++) {
 		if (likely (itd->index[uframe] == -1))
 			continue;
+
 		urb_index = itd->index[uframe];
 		desc = &urb->iso_frame_desc [urb_index];
 
@@ -1603,45 +1605,53 @@ itd_complete (
 			desc->status = 0;
 			desc->actual_length = EHCI_ITD_LENGTH (t);
 		}
+
+		/* handle completion now? */
+		if (urb_index + 1 == urb->number_of_packets) {
+			struct usb_device *dev;
+
+			usb_put_urb (urb);
+			/* ASSERT: it's really the last itd for this urb
+			   struct ehci_itd	*stritd;
+			   list_for_each_entry (stritd, &stream->td_list, itd_list)
+			   BUG_ON (stritd != itd && stritd->urb == urb);
+			*/
+			itd->urb = NULL;
+
+			/* give urb back to the driver ... can be out-of-order */
+			dev = urb->dev;
+			ehci_urb_done (ehci, urb);
+			urb = NULL;
+
+			ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
+
+			if (unlikely (list_empty (&stream->td_list))) {
+				ehci_to_hcd(ehci)->self.bandwidth_allocated
+					-= stream->bandwidth;
+				ehci_vdbg (ehci,
+					   "deschedule devp %s ep%d%s-iso\n",
+					   dev->devpath, stream->bEndpointAddress & 0x0f,
+					   (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
+			}
+			iso_stream_put (ehci, stream);
+		}
 	}
 
-	usb_put_urb (urb);
-	itd->urb = NULL;
+	itd->uframe_scanned = uframe_after;
+	if (uframe_after < 8)
+		return false;
+
 	itd->stream = NULL;
+	itd->uframe_scanned = 0;
 	list_move (&itd->itd_list, &stream->free_list);
 	iso_stream_put (ehci, stream);
 
-	/* handle completion now? */
-	if (likely ((urb_index + 1) != urb->number_of_packets))
-		return 0;
-
-	/* ASSERT: it's really the last itd for this urb
-	list_for_each_entry (itd, &stream->td_list, itd_list)
-		BUG_ON (itd->urb == urb);
-	 */
-
-	/* give urb back to the driver ... can be out-of-order */
-	dev = urb->dev;
-	ehci_urb_done (ehci, urb);
-	urb = NULL;
-
 	/* defer stopping schedule; completion can submit */
 	ehci->periodic_sched--;
 	if (unlikely (!ehci->periodic_sched))
 		(void) disable_periodic (ehci);
-	ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
 
-	if (unlikely (list_empty (&stream->td_list))) {
-		ehci_to_hcd(ehci)->self.bandwidth_allocated
-				-= stream->bandwidth;
-		ehci_vdbg (ehci,
-			"deschedule devp %s ep%d%s-iso\n",
-			dev->devpath, stream->bEndpointAddress & 0x0f,
-			(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
-	}
-	iso_stream_put (ehci, stream);
-
-	return 1;
+	return true;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -2156,30 +2166,18 @@ restart:
 				q = q.fstn->fstn_next;
 				break;
 			case Q_TYPE_ITD:
-				/* skip itds for later in the frame */
 				rmb ();
-				for (uf = live ? uframes : 8; uf < 8; uf++) {
-					if (0 == (q.itd->hw_transaction [uf]
-							& ITD_ACTIVE(ehci)))
-						continue;
+				modified = itd_scan(ehci, q.itd, uf = live ? uframes : 8);
+				if (!modified) {
 					q_p = &q.itd->itd_next;
 					hw_p = &q.itd->hw_next;
-					type = Q_NEXT_TYPE(ehci,
-							q.itd->hw_next);
-					q = *q_p;
-					break;
+				} else {
+					*q_p = q.itd->itd_next;
+					*hw_p = q.itd->hw_next;
 				}
-				if (uf != 8)
-					break;
-
-				/* this one's ready ... HC won't cache the
-				 * pointer for much longer, if at all.
-				 */
-				*q_p = q.itd->itd_next;
-				*hw_p = q.itd->hw_next;
 				type = Q_NEXT_TYPE(ehci, q.itd->hw_next);
+
 				wmb();
-				modified = itd_complete (ehci, q.itd);
 				q = *q_p;
 				break;
 			case Q_TYPE_SITD:
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 951d69f..df35964 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -581,6 +581,7 @@ struct ehci_itd {
 	struct urb		*urb;
 	struct ehci_iso_stream	*stream;	/* endpoint's queue */
 	struct list_head	itd_list;	/* list of stream's itds */
+	unsigned		uframe_scanned;
 
 	/* any/all hw_transactions here may be used by that urb */
 	unsigned		frame;		/* where scheduled */
-- 
1.5.3.3


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

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

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

* [PATCH] US-122L Driver, v0
  2007-11-07 15:41 Fwd: [PATCH] ehci-hcd: complete iso urbs ASAP for number_of_packets != (n * 8) also Karsten Wiese
@ 2007-11-07 18:55 ` Karsten Wiese
  2007-11-07 19:36   ` Karsten Wiese
  2007-11-08  7:24   ` Clemens Ladisch
  0 siblings, 2 replies; 7+ messages in thread
From: Karsten Wiese @ 2007-11-07 18:55 UTC (permalink / raw)
  To: alsa-devel

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

Hi,

attached patches implement a simple driver for the tascam us-122l.
Everything except midi out works i think ;-)

For USB 2.0 you also have to aply
	http://marc.info/?l=linux-usb-devel&m=119444869917392&w=2
named "ehci-hcd: complete iso urbs ASAP ..."

Will post a jack driver to use it soon.
so far you can #define DEBUG_USB_STREAM in usb_stream.c to 
playback what is captured.

no alsa pcm interface there yet. only a hwdep to talk to the
"usb stream" thing.

A userspace plugin that works like the jack driver would be fine too.
Is that easy to do? I mean that userspace driver should export all 
those standard alsa pcm interfaces then.

Comments bug/success reports, patches welcome.

      Karsten

[-- Attachment #2: 0001-Driver-for-the-US-122L-usb-soundcard.patch --]
[-- Type: text/x-diff, Size: 18455 bytes --]

From 0a9a1787bad19a5f542a11f4f087e83951cd2d00 Mon Sep 17 00:00:00 2001
From: Karsten Wiese <fzu@wemgehoertderstaat.de>
Date: Wed, 7 Nov 2007 19:36:14 +0100
Subject: [PATCH 1/1] Driver for the US-122L usb soundcard
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit

uses the "usb stream" facility for pcm. The "usb stream" is made mmap()able
by means of a hwdep device.
A patch to read/write audio from userspace will follow as a jackd driver.

For midi in snd-usb-lib is used. Patch for the needed quirk follows.
Midi out doesn't work yet.

Works only with USB1.1 host so far. For ehci a patch is needed so
"Usb stream" doesn't have to cut the urbs on 8 µframe boundaries.
That patch has been posted to usb-devel & alsa-devel as
"ehci-hcd: complete iso urbs ASAP for number_of_packets != (n * 8) also"

Signed-off-by: Karsten Wiese <fzu@wemgehoertderstaat.de>
---
 include/sound/asound.h   |    3 +-
 sound/usb/Kconfig        |   11 +
 sound/usb/usbaudio.h     |    2 +-
 sound/usb/usx2y/Makefile |    2 +
 sound/usb/usx2y/us122l.c |  512 ++++++++++++++++++++++++++++++++++++++++++++++
 sound/usb/usx2y/us122l.h |   21 ++
 6 files changed, 549 insertions(+), 2 deletions(-)
 create mode 100644 sound/usb/usx2y/us122l.c
 create mode 100644 sound/usb/usx2y/us122l.h

diff --git a/include/sound/asound.h b/include/sound/asound.h
index c1621c6..f163f66 100644
--- a/include/sound/asound.h
+++ b/include/sound/asound.h
@@ -92,9 +92,10 @@ enum {
 	SNDRV_HWDEP_IFACE_USX2Y_PCM,	/* Tascam US122, US224 & US428 rawusb pcm */
 	SNDRV_HWDEP_IFACE_PCXHR,	/* Digigram PCXHR */
 	SNDRV_HWDEP_IFACE_SB_RC,	/* SB Extigy/Audigy2NX remote control */
+	SNDRV_HWDEP_IFACE_USB_STREAM,	/* direct access to usb stream */
 
 	/* Don't forget to change the following: */
-	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_SB_RC
+	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_USB_STREAM
 };
 
 struct snd_hwdep_info {
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
index 315360f..3da2fb4 100644
--- a/sound/usb/Kconfig
+++ b/sound/usb/Kconfig
@@ -57,5 +57,16 @@ config SND_USB_CAIAQ_INPUT
 	   * Native Instruments RigKontrol2
 	   * Native Instruments Audio Kontrol 1
 
+config SND_USB_US122L
+	tristate "Tascam US-122L USB driver"
+	depends on SND && USB && (X86 || PPC || ALPHA)
+	select SND_RAWMIDI
+	help
+	  Say Y here to include support for Tascam USB Audio/MIDI
+	  interfaces or controllers US-122L.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-usb-us122l.
+
 endmenu
 
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 2272f45..23b13f4 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -160,7 +160,7 @@ enum quirk_type {
 	QUIRK_AUDIO_EDIROL_UA700_UA25,
 	QUIRK_AUDIO_EDIROL_UA1000,
 	QUIRK_AUDIO_EDIROL_UA101,
-
+	QUIRK_MIDI_US122L,
 	QUIRK_TYPE_COUNT
 };
 
diff --git a/sound/usb/usx2y/Makefile b/sound/usb/usx2y/Makefile
index 9ac22bc..4b537ff 100644
--- a/sound/usb/usx2y/Makefile
+++ b/sound/usb/usx2y/Makefile
@@ -1,3 +1,5 @@
 snd-usb-usx2y-objs := usbusx2y.o usX2Yhwdep.o usx2yhwdeppcm.o
+snd-usb-us122l-objs := us122l.o usb_stream.o
 
 obj-$(CONFIG_SND_USB_USX2Y) += snd-usb-usx2y.o
+obj-$(CONFIG_SND_USB_US122L) += snd-usb-us122l.o
\ No newline at end of file
diff --git a/sound/usb/usx2y/us122l.c b/sound/usb/usx2y/us122l.c
new file mode 100644
index 0000000..ac9a46d
--- /dev/null
+++ b/sound/usb/usx2y/us122l.c
@@ -0,0 +1,512 @@
+/*
+ * Copyright (c) 2007 by Karsten Wiese <fzu@wemgehoertderstaat.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/hwdep.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <linux/usb.h>
+#include "usb_stream.h"
+#include "../usbaudio.h"
+#include "us122l.h"
+
+MODULE_AUTHOR("Karsten Wiese <fzu@wemgehoertderstaat.de>");
+MODULE_DESCRIPTION("TASCAM "NAME_ALLCAPS" Version 0.1");
+MODULE_LICENSE("GPL");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
+static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for "NAME_ALLCAPS".");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for "NAME_ALLCAPS".");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable "NAME_ALLCAPS".");
+
+static int snd_us122l_card_used[SNDRV_CARDS];
+
+
+static int us122l_create_usbmidi(struct snd_card *card)
+{
+	static struct snd_usb_midi_endpoint_info quirk_data = {
+		.out_ep = 4,
+		.in_ep = 3,
+		.out_cables =	0x001,
+		.in_cables =	0x001
+	};
+	static struct snd_usb_audio_quirk quirk = {
+		.vendor_name =	"US122L",
+		.product_name =	NAME_ALLCAPS,
+		.ifnum = 	1,
+       		.type = QUIRK_MIDI_US122L,
+		.data = &quirk_data
+	};
+	struct usb_device *dev = US122L(card)->chip.dev;
+	struct usb_interface *iface = usb_ifnum_to_if(dev, 1);
+
+	return snd_usb_create_midi_interface(&US122L(card)->chip, iface, &quirk);
+}
+
+/*
+ * Wrapper for usb_control_msg().
+ * Allocates a temp buffer to prevent dmaing from/to the stack.
+ */
+static int us122l_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
+		    __u8 requesttype, __u16 value, __u16 index, void *data,
+		    __u16 size, int timeout)
+{
+	int err;
+	void *buf = NULL;
+
+	if (size > 0) {
+		buf = kmemdup(data, size, GFP_KERNEL);
+		if (!buf)
+			return -ENOMEM;
+	}
+	err = usb_control_msg(dev, pipe, request, requesttype,
+			      value, index, buf, size, timeout);
+	if (size > 0) {
+		memcpy(data, buf, size);
+		kfree(buf);
+	}
+	return err;
+}
+
+static int us122l_set_sample_rate(struct usb_device *dev, int rate)
+{
+	unsigned int ep = 0x81;
+	unsigned char data[3];
+	int err;
+
+		data[0] = rate;
+		data[1] = rate >> 8;
+		data[2] = rate >> 16;
+		if ((err = us122l_ctl_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR,
+					   USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
+					   SAMPLING_FREQ_CONTROL << 8, ep, data, 3, 1000)) < 0) {
+			snd_printk(KERN_ERR "%d: cannot set freq %d to ep 0x%x\n",
+				   dev->devnum, rate, ep);
+			return err;
+		}
+
+	return 0;
+}
+
+
+static struct page * usb_stream_hwdep_vm_nopage(struct vm_area_struct *area, unsigned long address, int *type)
+{
+	unsigned long offset;
+	struct page *page;
+	void *vaddr;
+	struct us122l *us122l = area->vm_private_data;
+
+	offset = area->vm_pgoff << PAGE_SHIFT;
+	offset += address - area->vm_start;
+	snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_OOM);
+	if (offset < PAGE_ALIGN(us122l->usb_stream->read_size)) {
+		vaddr = (char *)us122l->usb_stream + offset;
+		printk(KERN_WARNING"%s:%i 0x%lx\n", __FUNCTION__, __LINE__, offset);
+	} else {
+		vaddr = (char *)us122l->usb_stream->write_page +
+			offset - PAGE_ALIGN(us122l->usb_stream->read_size);
+		printk(KERN_WARNING"%s:%i 0x%lx\n", __FUNCTION__, __LINE__,
+		       offset - PAGE_ALIGN(us122l->usb_stream->read_size));
+	}
+
+	page = virt_to_page(vaddr);
+	get_page(page);
+
+	if (type)
+		*type = VM_FAULT_MINOR;
+
+	return page;
+}
+
+
+static struct vm_operations_struct usb_stream_hwdep_vm_ops = {
+	.nopage = usb_stream_hwdep_vm_nopage,
+};
+
+
+static int usb_stream_hwdep_open(struct snd_hwdep *hw, struct file *file)
+{
+	printk(KERN_DEBUG"%s:%i\n", __FUNCTION__, __LINE__);
+	return 0;
+}
+
+static int usb_stream_hwdep_mmap(struct snd_hwdep *hw, struct file *filp, struct vm_area_struct *area)
+{
+	unsigned long	size = area->vm_end - area->vm_start;
+	struct us122l	*us122l = hw->private_data;
+	unsigned long offset;
+	struct usb_stream *s = us122l->usb_stream;
+	bool read;
+
+	offset = area->vm_pgoff << PAGE_SHIFT;
+	read = offset < s->read_size;
+	if (read && area->vm_flags & VM_WRITE)
+		return -EPERM;
+
+	printk(KERN_WARNING"%s %lu %u\n", __FUNCTION__, size, read ? s->read_size : s->write_size); 
+	/* if userspace tries to mmap beyond end of our buffer, fail */ 
+	if (size > PAGE_ALIGN(read ? s->read_size : s->write_size)) {
+		printk(KERN_WARNING"%lu > %u\n", size, read ? s->read_size : s->write_size); 
+		return -EINVAL;
+	}
+
+
+	area->vm_ops = &usb_stream_hwdep_vm_ops;
+	area->vm_flags |= VM_RESERVED;
+	area->vm_private_data = us122l;
+	return 0;
+}
+
+static unsigned int usb_stream_hwdep_poll(struct snd_hwdep *hw, struct file *file, poll_table *wait)
+{
+	struct us122l	*us122l = hw->private_data;
+	struct usb_stream *s = us122l->usb_stream;
+	unsigned int mask;
+
+	poll_wait(file, &s->sleep, wait);
+
+	switch (s->state) {
+	case usb_stream_ready:
+		if (s->periods_polled != s->periods_done) {
+			s->periods_polled = s->periods_done;
+			mask = POLLIN | POLLOUT | POLLWRNORM;
+			break;
+		}
+		/* Fall through */
+		mask = 0;
+		break;
+	default:
+		mask = POLLIN | POLLOUT | POLLWRNORM | POLLERR;
+		break;
+	}
+	return mask;
+}
+
+static void us122l_stop(struct us122l *us122l)
+{
+	struct list_head *p;
+	list_for_each(p, &us122l->chip.midi_list)
+		snd_usbmidi_input_stop(p);
+
+	usb_stream_stop(us122l->usb_stream);
+	usb_stream_free(us122l->usb_stream);
+	us122l->usb_stream = NULL;
+}
+static bool us122l_start(struct us122l *us122l, unsigned rate, unsigned period_frames)
+{
+	struct list_head *p;
+	int err;
+	us122l->usb_stream = usb_stream_new(us122l->chip.dev,
+					    1, 2, rate, period_frames, 6);
+	if (!us122l->usb_stream)
+		return false;
+	printk(KERN_DEBUG"%s:%i\n", __FUNCTION__, __LINE__);
+	msleep(130);
+	msleep(30);
+	printk(KERN_DEBUG"%s:%i\n", __FUNCTION__, __LINE__);
+	err = us122l_set_sample_rate(us122l->chip.dev, rate);
+	printk(KERN_DEBUG"%s:%i\n", __FUNCTION__, __LINE__);
+
+	if (err) {
+		snd_printk(KERN_ERR "us144_set_sample_rate error \n");
+		return false;
+	}
+	if ((err = usb_stream_start(us122l->usb_stream)) < 0) {
+		us122l_stop(us122l);
+		snd_printk(KERN_ERR "us122l_start error %i \n", err);
+		return false;
+	}
+
+	list_for_each(p, &us122l->chip.midi_list)
+		snd_usbmidi_input_start(p);
+
+	return true;
+}
+
+static long usb_stream_hwdep_write(struct snd_hwdep *hw, const char __user *buf,
+				   long count, loff_t *offset)
+{
+	struct usb_stream_config cfg;
+	struct us122l *us122l;
+
+	if (count != sizeof(cfg))
+		return -EFAULT;
+
+	if (copy_from_user(&cfg, buf, sizeof(cfg)))
+		return -EFAULT;
+
+	if (cfg.version != USB_STREAM_INTERFACE_VERSION ||
+	    (cfg.sample_rate != 44100 && cfg.sample_rate != 48000) ||
+	    cfg.frame_size != 6 ||
+	    cfg.period_frames < 64 || cfg.period_frames > 1024)
+		return -EFAULT;
+
+	us122l = hw->private_data;
+
+	us122l_stop(us122l);
+	msleep(530);
+	if (!us122l_start(us122l, cfg.sample_rate, cfg.period_frames))
+		return -EFAULT;
+
+	return count;
+}
+
+#define SND_USB_STREAM_ID "USB STREAM"
+static int usb_stream_hwdep_new(struct snd_card *card)
+{
+	int err;
+	struct snd_hwdep *hw;
+	struct usb_device *dev = US122L(card)->chip.dev;
+
+	if ((err = snd_hwdep_new(card, SND_USB_STREAM_ID, 0, &hw)) < 0)
+		return err;
+
+	hw->iface = SNDRV_HWDEP_IFACE_USB_STREAM;
+	hw->private_data = US122L(card);
+	hw->ops.open = usb_stream_hwdep_open;
+	hw->ops.write = usb_stream_hwdep_write;
+ 	hw->ops.mmap = usb_stream_hwdep_mmap;
+	hw->ops.poll = usb_stream_hwdep_poll;
+	hw->exclusive = 1;
+	sprintf(hw->name, "/proc/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
+	return 0;
+}
+
+
+static bool us122l_create_card(struct snd_card *card)
+{
+	int err;
+	struct us122l *us122l = US122L(card);
+	printk(KERN_DEBUG"%s:%i\n", __FUNCTION__, __LINE__);
+
+	err = usb_set_interface(us122l->chip.dev, 1, 1);
+	if (err) {
+		snd_printk(KERN_ERR "usb_set_interface error \n");
+		return false;
+	}
+	if (!us122l_start(us122l, 44100, 256))
+		return false;
+
+	if ((err = us122l_create_usbmidi(card)) < 0) {
+		snd_printk(KERN_ERR "us122l_create_usbmidi error %i \n", err);
+		us122l_stop(us122l);
+		return false;
+	}
+
+	if ((err = usb_stream_hwdep_new(card)) < 0) {
+/* release the midi resources */
+		struct list_head *p;
+		list_for_each(p, &us122l->chip.midi_list)
+			snd_usbmidi_disconnect(p);
+
+		us122l_stop(us122l);
+		return false;
+	}
+	return true;
+} 
+
+static struct snd_card *usx2y_create_card(struct usb_device *device)
+{
+	int		dev;
+	struct snd_card *	card;
+	for (dev = 0; dev < SNDRV_CARDS; ++dev)
+		if (enable[dev] && !snd_us122l_card_used[dev])
+			break;
+	if (dev >= SNDRV_CARDS)
+		return NULL;
+	card = snd_card_new(index[dev], id[dev], THIS_MODULE, sizeof(struct us122l));
+	if (!card)
+		return NULL;
+	snd_us122l_card_used[US122L(card)->chip.index = dev] = 1;
+
+	US122L(card)->chip.dev = device;
+	US122L(card)->chip.card = card;
+	mutex_init(&US122L(card)->prepare_mutex);
+	INIT_LIST_HEAD(&US122L(card)->chip.midi_list);
+	strcpy(card->driver, "USB "NAME_ALLCAPS"");
+	sprintf(card->shortname, "TASCAM "NAME_ALLCAPS"");
+	sprintf(card->longname, "%s (%x:%x if %d at %03d/%03d)",
+		card->shortname, 
+		le16_to_cpu(device->descriptor.idVendor),
+		le16_to_cpu(device->descriptor.idProduct),
+		0,//us428(card)->usbmidi.ifnum,
+		US122L(card)->chip.dev->bus->busnum, US122L(card)->chip.dev->devnum
+		);
+	snd_card_set_dev(card, &device->dev);
+	return card;
+}
+
+static void *us122l_usb_probe(struct usb_interface *intf, const struct usb_device_id *device_id)
+{
+	struct snd_card *card;
+	struct usb_device *device = interface_to_usbdev(intf);
+	if (!(card = usx2y_create_card(device)))
+		return NULL;
+	if (!us122l_create_card(card) ||
+	    snd_card_register(card) < 0) {
+		snd_card_free(card);
+		return NULL;
+	}
+
+	usb_get_dev(device);
+	return card;
+}
+
+static int snd_us122l_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct snd_card *card;
+	printk(KERN_DEBUG"%s:%i:%p:%i\n", __FUNCTION__, __LINE__, intf, intf->cur_altsetting->desc.bInterfaceNumber);
+	if (intf->cur_altsetting->desc.bInterfaceNumber != 1)
+		return -EIO;
+
+	card = us122l_usb_probe(usb_get_intf(intf), id);
+
+	if (card) {
+		usb_set_intfdata(intf, card);
+		return 0;
+	}
+
+	usb_put_intf(intf);
+	return -EIO;
+}
+
+static void snd_us122l_disconnect(struct usb_interface *intf)
+{
+	struct snd_card *card;
+	struct us122l *us122l;
+	struct list_head *p;
+
+	card = usb_get_intfdata(intf);
+	if (!card)
+		return;
+	usb_set_intfdata(intf, NULL);
+	printk(KERN_DEBUG"%s:%i\n", __FUNCTION__, __LINE__);
+	
+	snd_card_disconnect(card);
+
+	us122l = US122L(card);
+	us122l_stop(us122l);
+	us122l->chip.shutdown = 1;
+	
+/* release the midi resources */
+	list_for_each(p, &us122l->chip.midi_list) {
+		snd_usbmidi_disconnect(p);
+	}
+
+	usb_put_intf(intf);
+	usb_put_dev(US122L(card)->chip.dev);
+
+	snd_card_free(card);
+}
+
+static int snd_us122l_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct snd_card *card;
+	struct us122l *us122l;
+	struct list_head *p;
+
+	card = dev_get_drvdata(&intf->dev);
+	if (!card)
+		return 0;
+
+	us122l = US122L(card);
+	if (!us122l)
+		return 0;
+
+	list_for_each(p, &us122l->chip.midi_list)
+		snd_usbmidi_input_stop(p);
+
+	usb_stream_stop(us122l->usb_stream);
+
+	return 0;
+}
+
+static int snd_us122l_resume(struct usb_interface *intf)
+{
+	struct snd_card *card;
+	struct us122l *us122l;
+	struct list_head *p;
+	int err;
+	printk(KERN_DEBUG"%s:%i\n", __FUNCTION__, __LINE__);
+	card = dev_get_drvdata(&intf->dev);
+	if (!card)
+		return 0;
+
+	us122l = US122L(card);
+	if (!us122l)
+		return 0;
+
+	err = usb_set_interface(us122l->chip.dev, 1, 1);
+	if (err) {
+		snd_printk(KERN_ERR "usb_set_interface error \n");
+		return err;
+	}
+	if ((err = usb_stream_start(us122l->usb_stream)))
+	    return err;
+
+	list_for_each(p, &us122l->chip.midi_list)
+		snd_usbmidi_input_start(p);
+
+	return 0;
+}
+
+static struct usb_device_id snd_us122l_usb_id_table[] = {
+	{
+		.match_flags =	USB_DEVICE_ID_MATCH_DEVICE,
+		.idVendor =	0x0644,
+		.idProduct =	USB_ID_US122L
+	},
+/*  	{ */
+/* 		.match_flags =	USB_DEVICE_ID_MATCH_DEVICE, */
+/* 		.idVendor =	0x0644, */
+/* 		.idProduct =	USB_ID_US144 */
+/* 	}, */
+	{ /* terminator */ }
+};
+
+MODULE_DEVICE_TABLE(usb, snd_us122l_usb_id_table);
+static struct usb_driver snd_us122l_usb_driver = {
+	.name =		"snd-usb-us122l",
+	.probe =	snd_us122l_probe,
+	.disconnect =	snd_us122l_disconnect,
+	.suspend =	snd_us122l_suspend,
+	.resume =	snd_us122l_resume,
+	.reset_resume =	snd_us122l_resume,
+	.id_table =	snd_us122l_usb_id_table,
+};
+
+
+static int __init snd_us122l_module_init(void)
+{
+	return usb_register(&snd_us122l_usb_driver);
+}
+
+static void __exit snd_us122l_module_exit(void)
+{
+	usb_deregister(&snd_us122l_usb_driver);
+}
+
+module_init(snd_us122l_module_init)
+module_exit(snd_us122l_module_exit)
diff --git a/sound/usb/usx2y/us122l.h b/sound/usb/usx2y/us122l.h
new file mode 100644
index 0000000..c046943
--- /dev/null
+++ b/sound/usb/usx2y/us122l.h
@@ -0,0 +1,21 @@
+#ifndef US122L_H
+#define US122L_H
+
+
+struct us122l {
+	struct snd_usb_audio 	chip;
+	int			stride;
+	struct usb_stream	*usb_stream;
+
+	struct mutex		prepare_mutex;
+};
+
+
+#define US122L(c) ((struct us122l *)(c)->private_data)
+
+#define NAME_ALLCAPS "US-122L"
+
+#define USB_ID_US122L 0x800E
+#define USB_ID_US144 0x800F
+
+#endif
-- 
1.5.3.3


[-- Attachment #3: 0001-Midi-in-support-for-the-US-122L-usb-soundcard.patch --]
[-- Type: text/x-diff, Size: 2006 bytes --]

From 1a4fe60461598309dac69cbcb4300244c1ae3459 Mon Sep 17 00:00:00 2001
From: Karsten Wiese <fzu@wemgehoertderstaat.de>
Date: Tue, 6 Nov 2007 21:37:12 +0100
Subject: [PATCH 1/1] Midi in support for the US-122L usb soundcard

The US-122L sends midi in data in 9 byte long packets.
Only the bytes up to before the first 0xFD are midi data.

Signed-off-by: Karsten Wiese <fzu@wemgehoertderstaat.de>
---
 sound/usb/usbmidi.c |   19 +++++++++++++++++++
 1 files changed, 19 insertions(+), 0 deletions(-)

diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c
index 99295f9..8a8e97f 100644
--- a/sound/usb/usbmidi.c
+++ b/sound/usb/usbmidi.c
@@ -621,6 +621,18 @@ static void snd_usbmidi_raw_input(struct snd_usb_midi_in_endpoint* ep,
 	snd_usbmidi_input_data(ep, 0, buffer, buffer_length);
 }
 
+static void snd_usbmidi_us122l_input(struct snd_usb_midi_in_endpoint* ep,
+				  uint8_t* buffer, int buffer_length)
+{
+	if (buffer_length != 9)
+		return;
+	buffer_length = 8;
+	while (buffer_length && buffer[buffer_length - 1] == 0xFD)
+		buffer_length--;
+	if (buffer_length)
+		snd_usbmidi_input_data(ep, 0, buffer, buffer_length);
+}
+
 static void snd_usbmidi_raw_output(struct snd_usb_midi_out_endpoint* ep)
 {
 	int count;
@@ -642,6 +654,11 @@ static struct usb_protocol_ops snd_usbmidi_raw_ops = {
 	.output = snd_usbmidi_raw_output,
 };
 
+static struct usb_protocol_ops snd_usbmidi_122l_ops = {
+	.input = snd_usbmidi_us122l_input,
+	.output = snd_usbmidi_raw_output,
+};
+
 /*
  * Emagic USB MIDI protocol: raw MIDI with "F5 xx" port switching.
  */
@@ -1661,6 +1678,8 @@ int snd_usb_create_midi_interface(struct snd_usb_audio* chip,
 			umidi->usb_protocol_ops =
 				&snd_usbmidi_maudio_broken_running_status_ops;
 		break;
+	case QUIRK_MIDI_US122L:
+		umidi->usb_protocol_ops = &snd_usbmidi_122l_ops;
 	case QUIRK_MIDI_FIXED_ENDPOINT:
 		memcpy(&endpoints[0], quirk->data,
 		       sizeof(struct snd_usb_midi_endpoint_info));
-- 
1.5.3.3


[-- Attachment #4: 0001-usb-stream-for-isochrone-usb-i-o.patch --]
[-- Type: text/x-diff, Size: 24104 bytes --]

From 27e99f95786b5765685cbc16337b2f4e05e01a48 Mon Sep 17 00:00:00 2001
From: Karsten Wiese <fzu@wemgehoertderstaat.de>
Date: Wed, 7 Nov 2007 19:27:14 +0100
Subject: [PATCH 1/1] "usb stream" for isochrone usb i/o

Provides a combination of an in pipe isochron stream with an out pipe one.
The out pipe's urbs are scheduled with packet lengths taken from the inpipe's
urbs just red.

A client reads/writes data in equally sized chunks. The chunks are specified
as "no of frames per transfer" with frame_size during initialisation.

Signed-off-by: Karsten Wiese <fzu@wemgehoertderstaat.de>
---
 sound/usb/usx2y/usb_stream.c |  676 ++++++++++++++++++++++++++++++++++++++++++
 sound/usb/usx2y/usb_stream.h |  111 +++++++
 2 files changed, 787 insertions(+), 0 deletions(-)
 create mode 100644 sound/usb/usx2y/usb_stream.c
 create mode 100644 sound/usb/usx2y/usb_stream.h

diff --git a/sound/usb/usx2y/usb_stream.c b/sound/usb/usx2y/usb_stream.c
new file mode 100644
index 0000000..7440df3
--- /dev/null
+++ b/sound/usb/usx2y/usb_stream.c
@@ -0,0 +1,676 @@
+/*
+ * Copyright (c) 2007 by Karsten Wiese <fzu@wemgehoertderstaat.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/usb.h>
+
+#include "usb_stream.h"
+
+
+/*                             setup                                  */
+
+static unsigned usb_stream_next_packet_size(struct usb_stream *s)
+{
+	s->out_phase_peeked = (s->out_phase & 0xffff) + s->freqn;
+	return (s->out_phase_peeked >> 16) * s->cfg.frame_size;
+}
+
+static void playback_prep_freqn(struct usb_stream *s, struct urb *urb)
+{
+	unsigned l = 0;
+	int pack;
+
+	urb->iso_frame_desc[0].length =	usb_stream_next_packet_size(s);
+	s->out_phase = s->out_phase_peeked;
+	urb->transfer_buffer_length = urb->iso_frame_desc[0].length;
+
+	for (pack = 1; pack < s->max_packs; pack++) {
+		l = usb_stream_next_packet_size(s);
+		if (s->idle_outsize + urb->transfer_buffer_length + l >
+		    s->period_size)
+			goto check;
+
+		s->out_phase = s->out_phase_peeked;
+		urb->iso_frame_desc[pack].offset = urb->transfer_buffer_length;
+		urb->iso_frame_desc[pack].length = l;
+		urb->transfer_buffer_length += l;
+	}
+	printk(KERN_WARNING"%s:%i %i\n", __FUNCTION__, __LINE__, urb->transfer_buffer_length);
+
+check:
+	urb->number_of_packets = pack;
+	s->idle_outsize += urb->transfer_buffer_length - s->period_size;
+	printk(KERN_WARNING"%s:%i idle=%i ul=%i ps=%i\n",
+	       __FUNCTION__, __LINE__,
+	       s->idle_outsize, urb->transfer_buffer_length, s->period_size);
+}
+
+static void init_pipe_urbs(struct usb_stream *s,
+			   struct urb **urbs, char *transfer,
+			   struct usb_device *dev, int pipe)
+{
+	int u, p;
+	int maxpacket = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+	int transfer_length = maxpacket * s->max_packs;
+
+	for (u = 0; u < USB_STREAM_NURBS;
+	     ++u, transfer += transfer_length) {
+		struct urb *urb = urbs[u];
+		struct usb_iso_packet_descriptor *desc;
+		usb_init_urb(urb);
+		urb->transfer_flags = URB_ISO_ASAP;
+		urb->transfer_buffer = transfer;
+		urb->dev = dev;
+		urb->pipe = pipe;
+		urb->number_of_packets = s->max_packs;
+		urb->context = s;
+		urb->interval = 1;
+		if (usb_pipeout(pipe))
+			continue;
+
+		urb->transfer_buffer_length = transfer_length;
+		desc = urb->iso_frame_desc;
+		desc->length = maxpacket;
+		for (p = 1; p < s->max_packs; ++p) {
+			desc[p].offset = desc[p - 1].offset + maxpacket;
+			desc[p].length = maxpacket;
+		}
+	}
+}
+
+static void init_urbs(struct usb_stream *s,
+		      struct usb_device *dev, int in_pipe, int out_pipe)
+{
+	int u, urbsize;
+	s->inurb[0] = (void *)((char *)s + sizeof(*s));
+	urbsize = sizeof(struct urb) +
+		sizeof(struct usb_iso_packet_descriptor) * s->max_packs;
+	for (u = 1; u < USB_STREAM_NURBS; ++u)
+		s->inurb[u] = (void *)((char *)s->inurb[u - 1] + urbsize);
+
+	s->outurb[0] = (void *)((char *)s->inurb[USB_STREAM_NURBS-1] +
+				urbsize);
+	for (u = 1; u < USB_STREAM_NURBS; ++u)
+		s->outurb[u] = (void *)((char *)s->outurb[u - 1] + urbsize);
+
+	init_pipe_urbs(s, s->inurb,
+		       (char *)s->outurb[USB_STREAM_NURBS-1] + urbsize,
+		       dev, in_pipe);
+	init_pipe_urbs(s, s->outurb, s->write_page,
+		       dev, out_pipe);
+}
+
+
+/*
+ * convert a sampling rate into our full speed format (fs/1000 in Q16.16)
+ * this will overflow at approx 524 kHz
+ */
+static inline unsigned get_usb_full_speed_rate(unsigned rate)
+{
+	return ((rate << 13) + 62) / 125;
+}
+
+/*
+ * convert a sampling rate into USB high speed format (fs/8000 in Q16.16)
+ * this will overflow at approx 4 MHz
+ */
+static inline unsigned get_usb_high_speed_rate(unsigned rate)
+{
+	return ((rate << 10) + 62) / 125;
+}
+
+void usb_stream_free(struct usb_stream *s)
+{
+	if (!s)
+		return;
+	free_pages((unsigned long)s->write_page, get_order(s->write_size));
+	free_pages((unsigned long)s, get_order(s->read_size));
+}
+
+struct usb_stream *usb_stream_new(struct usb_device *dev,
+				  unsigned in_endpoint,unsigned out_endpoint,
+				  unsigned sample_rate,
+				  unsigned period_frames, unsigned frame_size)
+{
+	struct usb_stream *s = NULL;
+	int packs, max_packsize;
+	int in_pipe, out_pipe;
+	int read_size = sizeof(*s);
+	int write_size;
+	int usb_frames = dev->speed == USB_SPEED_HIGH ? 8000 : 1000;
+	int pg;
+
+	in_pipe = usb_rcvisocpipe(dev, in_endpoint);
+	out_pipe = usb_sndisocpipe(dev, out_endpoint);
+
+	read_size += USB_STREAM_NURBS * 2 * sizeof(struct urb);
+
+	max_packsize = usb_maxpacket(dev, in_pipe, 0);
+
+	/*
+		t_period = period_frames / sample_rate
+		iso_packs = t_period / t_iso_frame
+			= (period_frames / sample_rate) * (1 / t_iso_frame)
+	*/
+
+	packs = period_frames * usb_frames / sample_rate + 1;
+
+	if (dev->speed == USB_SPEED_HIGH)
+		packs = (packs + 7) & ~7;
+
+	read_size += (sizeof(struct usb_iso_packet_descriptor) + max_packsize) *
+		packs * USB_STREAM_NURBS;
+	read_size += sizeof(struct usb_iso_packet_descriptor) *
+		packs *	USB_STREAM_NURBS;
+
+	max_packsize = usb_maxpacket(dev, out_pipe, 1);
+	write_size = max_packsize * packs * USB_STREAM_NURBS;
+
+	if (read_size >= 128*PAGE_SIZE || write_size >= 128*PAGE_SIZE) {
+		printk(KERN_ERR"%s: a size exceeds 128*PAGE_SIZE\n", __FUNCTION__);
+		goto out;
+	}
+
+	pg = get_order(read_size);
+	if ((s = (void *) __get_free_pages(GFP_KERNEL|__GFP_COMP, pg)) == NULL) {
+		printk(KERN_ERR"%s: couldn't __get_free_pages()\n", __FUNCTION__);
+		goto out;
+	}
+	memset(s, 0, (read_size + PAGE_SIZE - 1) & PAGE_MASK);
+	s->cfg.version = USB_STREAM_INTERFACE_VERSION;
+	s->read_kernel_address = (unsigned long)s;
+	s->read_size = read_size;
+	s->urb_number_of_packets_offset = offsetof(struct urb, number_of_packets);
+	s->urb_iso_frame_desc_offset = offsetof(struct urb, iso_frame_desc);
+	s->urb_transfer_buffer_offset = offsetof(struct urb, transfer_buffer);
+
+	s->cfg.sample_rate = sample_rate;
+	s->cfg.frame_size = frame_size;
+	s->max_packs = packs;
+	s->cfg.period_frames = period_frames;
+	s->period_size = frame_size * period_frames;
+	init_waitqueue_head(&s->sleep);
+
+	s->write_size = write_size;
+	pg = get_order(write_size);
+	s->write_kernel_address = __get_free_pages(GFP_KERNEL|__GFP_COMP, pg);
+	if ((s->write_page = (void *)s->write_kernel_address) == NULL) {
+		printk(KERN_ERR"%s: couldn't __get_free_pages()\n", __FUNCTION__);
+		usb_stream_free(s);
+		return NULL;
+	}
+	memset(s->write_page, 0, (write_size + PAGE_SIZE - 1) & PAGE_MASK);
+
+	/* calculate the frequency in 16.16 format */
+	if (dev->speed == USB_SPEED_FULL)
+		s->freqn = get_usb_full_speed_rate(sample_rate);
+	else
+		s->freqn = get_usb_high_speed_rate(sample_rate);
+
+	init_urbs(s, dev, in_pipe, out_pipe);
+	s->state = usb_stream_stopped;
+out:
+	return s;
+}
+
+
+/*                             start                                  */
+
+static bool balance_check(struct usb_stream *s, struct urb *urb)
+{
+	bool r;
+	if (unlikely(urb->status)) {
+		printk(KERN_WARNING"%s: status=%i\n", __FUNCTION__, urb->status);
+		s->iso_frame_balance = 0x7FFFFFFF;
+		return false;
+	}
+	r = s->iso_frame_balance == 0;
+	if (!r)
+		s->i_urb = urb;
+	return r;
+}
+
+static bool balance_playback(struct urb *urb)
+{
+	struct usb_stream *s = urb->context;
+	s->iso_frame_balance += urb->number_of_packets;
+	return balance_check(s, urb);
+}
+
+static bool balance_capture(struct urb *urb)
+{
+	struct usb_stream *s = urb->context;
+	s->iso_frame_balance -= urb->number_of_packets;
+	return balance_check(s, urb);
+}
+
+static void subs_set_complete(struct urb ** urbs, void (*complete)(struct urb *))
+{
+	int u;
+
+	for (u = 0; u < USB_STREAM_NURBS; u++) {
+		struct urb *urb = urbs[u];
+		urb->complete = complete;
+	}
+}
+
+int usb_stream_prepare_playback(struct usb_stream *s, struct urb *inurb)
+{
+	struct urb *io;
+	struct usb_iso_packet_descriptor *id, *od;
+	int p, l = 0;
+
+	io = s->idle_outurb;
+	od = io->iso_frame_desc;
+	io->transfer_buffer_length = 0;
+
+	for (p = 0; s->sync_packet < 0; ++p, ++s->sync_packet) {
+		struct urb *ii = s->completed_inurb;
+		id = ii->iso_frame_desc + ii->number_of_packets + s->sync_packet;
+		l = id->actual_length;
+
+		od[p].length = l;
+		od[p].offset = io->transfer_buffer_length;
+		io->transfer_buffer_length += l;
+	}
+
+	for (;
+	     s->sync_packet < inurb->number_of_packets && p < s->max_packs;
+	     ++p, ++s->sync_packet) {
+		id = inurb->iso_frame_desc + s->sync_packet;
+		l = id->actual_length;
+
+		if (s->idle_outsize + io->transfer_buffer_length + l > s->period_size)
+			goto check_ok;
+
+		od[p].length = l;
+		od[p].offset = io->transfer_buffer_length;
+		io->transfer_buffer_length += l;
+	}
+
+check_ok:
+	s->sync_packet -= inurb->number_of_packets;
+	if (s->sync_packet < -2 || s->sync_packet > 0) {
+		printk(KERN_ERR"%s: invalid sync_packet = %i; p=%i nop=%i %i %x %x %x > %x %i %i\n",
+		       __FUNCTION__, s->sync_packet, p, inurb->number_of_packets, s->idle_outsize + io->transfer_buffer_length + l, s->idle_outsize , io->transfer_buffer_length,  l, s->period_size,
+		       (s->idle_outsize + io->transfer_buffer_length + l > s->period_size),
+		       (s->idle_outsize + io->transfer_buffer_length + (int)l > s->period_size));
+		return -1;
+	}
+	if (io->transfer_buffer_length % s->cfg.frame_size) {
+		printk(KERN_ERR"%s: invalid outsize = %i\n",
+		       __FUNCTION__, io->transfer_buffer_length);
+		return -1;
+	}
+	s->idle_outsize += io->transfer_buffer_length - s->period_size;
+	io->number_of_packets = p;
+	if (s->idle_outsize > 0) {
+		printk(KERN_DEBUG"%s:%i idle=%i\n", __FUNCTION__, __LINE__, s->idle_outsize);
+		return -1;
+	}
+	return 0;
+}
+
+static void prepare_inurb(int number_of_packets, struct urb *iu)
+{
+	struct usb_iso_packet_descriptor *id;
+	int p;
+
+	iu->number_of_packets = number_of_packets;
+	id = iu->iso_frame_desc;
+	id->offset = 0;
+	for (p = 0; p < iu->number_of_packets - 1; ++p)
+		id[p + 1].offset = id[p].offset + id[p].length;
+
+	iu->transfer_buffer_length =
+		id[0].length * iu->number_of_packets;
+}
+
+static int submit_urbs(struct usb_stream *s, struct urb *inurb, struct urb *outurb)
+{
+	int err;
+	prepare_inurb(s->idle_outurb->number_of_packets, s->idle_inurb);
+	if ((err = usb_submit_urb(s->idle_inurb, GFP_ATOMIC)) < 0) {
+		printk(KERN_WARNING"%s: usb_submit_urb()"
+		       " returned %i\n", __FUNCTION__, err);
+		return err;
+	}
+	s->idle_inurb = s->completed_inurb;
+	s->completed_inurb = inurb;
+	if ((err = usb_submit_urb(s->idle_outurb, GFP_ATOMIC)) < 0) {
+		printk(KERN_WARNING"%s: usb_submit_urb()"
+		       " returned %i\n", __FUNCTION__, err);
+		return err;
+	}
+	s->idle_outurb = s->completed_outurb;
+	s->completed_outurb = outurb;
+	return 0;
+}
+
+#ifdef DEBUG_LOOP_BACK
+/*
+  This loop_back() shows how to read/write the period data.
+ */
+static void loop_back(struct usb_stream *s)
+{
+	char *i, *o;
+	int il, ol, l, p;
+	struct urb *iu;
+	struct usb_iso_packet_descriptor *id;
+
+	o = s->playback1st_to;
+	ol = s->playback1st_size;
+	l = 0;
+
+	if (s->insplit_pack >= 0) {
+		iu = s->idle_inurb;
+		id = iu->iso_frame_desc;
+		p = s->insplit_pack;
+	} else
+		goto second;
+loop:
+	for (;p < iu->number_of_packets && l < s->period_size; ++p) {
+		i = iu->transfer_buffer + id[p].offset;
+		il = id[p].actual_length;
+		if (l + il > s->period_size)
+			il = s->period_size - l;
+		if (il <= ol) {
+			memcpy(o, i, il);
+			o += il;
+			ol -= il;
+		} else {
+			memcpy(o, i, ol);
+			singen_6pack(o, ol);
+			o = s->playback_to;
+			memcpy(o, i + ol, il - ol);
+			o += il - ol;
+			ol = s->period_size - s->playback1st_size;
+		}
+		l += il;
+	}
+	if (iu == s->completed_inurb) {
+		if (l != s->period_size)
+			printk(KERN_DEBUG"%s:%i %i\n", __FUNCTION__, __LINE__,
+			       l/(int)s->cfg.frame_size);
+
+		return;
+	}
+second:
+	iu = s->completed_inurb;
+	id = iu->iso_frame_desc;
+	p = 0;
+	goto loop;
+
+}
+#else
+static void loop_back(struct usb_stream *s)
+{
+}
+#endif
+
+static void stream_idle(struct usb_stream *s, struct urb *inurb, struct urb *outurb)
+{
+	int l, p;
+	int insize = s->idle_insize;
+	int urb_size = 0;
+
+	s->insplit_pack = s->next_insplit_pack;
+	s->insplit_offset = s->next_insplit_offset;
+	s->next_insplit_pack = -1;
+	for (p = 0; p < inurb->number_of_packets; ++p) {
+		struct usb_iso_packet_descriptor *id = inurb->iso_frame_desc;
+		l = id[p].actual_length;
+		if (unlikely(l == 0 || id[p].status)) {
+			printk(KERN_ERR"%s:%i underrun, status=%u\n", __FUNCTION__, __LINE__,
+			       id[p].status);
+			goto err_out;
+		}
+		if (insize + l > s->period_size) {
+			s->next_insplit_pack = p;
+			s->next_insplit_offset = s->period_size - insize;
+			insize = l - s->next_insplit_offset;
+		} else
+			insize += l;
+		urb_size += l;
+	}
+	s->idle_insize += urb_size - s->period_size;
+	if (s->idle_insize < 0) {
+		printk(KERN_DEBUG"%s:%i %i\n", __FUNCTION__, __LINE__,
+		       (s->idle_insize)/(int)s->cfg.frame_size);
+		goto err_out;
+	}
+	s->insize_done += urb_size;
+
+	l = s->idle_outsize;
+	s->playback1st_to = s->idle_outurb->transfer_buffer - l;
+
+	if (usb_stream_prepare_playback(s, inurb) < 0)
+		goto err_out;
+
+	if (s->insplit_pack >= 0) {
+		struct usb_iso_packet_descriptor *isd =
+			s->completed_inurb->iso_frame_desc + s->insplit_pack;
+		isd->offset += s->insplit_offset;
+		isd->actual_length -= s->insplit_offset;
+	}
+
+	s->playback1st_size = s->idle_outurb->transfer_buffer_length + l;
+	s->playback_to = s->completed_outurb->transfer_buffer;
+
+	if (submit_urbs(s, inurb, outurb) < 0)
+		goto err_out;
+
+	loop_back(s);
+	s->periods_done++;
+	wake_up(&s->sleep);
+	return;
+err_out:
+	s->state = usb_stream_xrun;
+}
+
+static void i_capture_idle(struct urb *urb)
+{
+	struct usb_stream *s = urb->context;
+	if (balance_capture(urb))
+		stream_idle(s, urb, s->i_urb);
+}
+
+static void i_playback_idle(struct urb *urb)
+{
+	struct usb_stream *s = urb->context;
+	if (balance_playback(urb))
+		stream_idle(s, s->i_urb, urb);
+}
+
+static void stream_start(struct usb_stream *s, struct urb *inurb, struct urb *outurb)
+{
+	if (s->state >= usb_stream_sync1) {
+		int l, p;
+		int urb_size = 0;
+
+		for (p = 0; p < inurb->number_of_packets; ++p) {
+			l = inurb->iso_frame_desc[p].actual_length;
+			urb_size += l;
+		}
+		if (s->idle_insize + urb_size < s->period_size) {
+			printk(KERN_DEBUG"%s:%i %i %i\n", __FUNCTION__, __LINE__,
+			       (s->idle_insize + urb_size)/(int)s->cfg.frame_size,
+			       s->insize_done/(int)s->cfg.frame_size);
+			s->idle_insize = s->period_size - urb_size + s->cfg.frame_size;
+			s->insize_done = -urb_size;
+		}
+		s->idle_insize += urb_size - s->period_size;
+		if (s->idle_insize < 0) {
+			printk(KERN_DEBUG"%s:%i %i\n", __FUNCTION__, __LINE__,
+ 			       (s->idle_insize)/(int)s->cfg.frame_size);
+			return;
+		}
+		s->insize_done += urb_size;
+
+		if (usb_stream_prepare_playback(s, inurb) < 0)
+			return;
+
+	} else
+		playback_prep_freqn(s, s->idle_outurb);
+
+	if (submit_urbs(s, inurb, outurb) < 0)
+		return;
+
+	if (s->state == usb_stream_sync1 && s->insize_done > 360000) {
+		/* 48000/s needs longer than 44100/s         ^^^^^^ */
+		s->state = usb_stream_ready;
+		subs_set_complete(s->inurb, i_capture_idle);
+		subs_set_complete(s->outurb, i_playback_idle);
+	}
+}
+
+static void i_capture_start(struct urb *urb)
+{
+	struct usb_stream *s = urb->context;
+	int pack;
+	int empty = 0;
+
+	if (urb->status) {
+		printk(KERN_WARNING"%s: status=%i\n", __FUNCTION__, urb->status);
+		return;
+	}
+
+	for (pack = 0; pack < urb->number_of_packets; ++pack) {
+		int l = urb->iso_frame_desc[pack].actual_length;
+		if (l < s->cfg.frame_size) {
+			++empty;
+			if (s->state >= usb_stream_sync0) {
+				printk(KERN_WARNING"%s:%i\n",
+				       __FUNCTION__, __LINE__);
+				return;
+			}
+		}
+
+	}
+
+	if (empty) {
+		printk(KERN_DEBUG"%s:%i: %i", __FUNCTION__, __LINE__,
+		       urb->iso_frame_desc[0].actual_length);
+		for (pack = 1; pack < urb->number_of_packets; ++pack) {
+			int l = urb->iso_frame_desc[pack].actual_length;
+			printk(" %i", l);
+		}
+		printk("\n");
+	}
+
+	if (!empty && s->state < usb_stream_sync1)
+		if (++s->state == usb_stream_sync1)
+			;//s->idle_outsize = 0;
+	if (balance_capture(urb))
+		stream_start(s, urb, s->i_urb);
+}
+
+static void i_playback_start(struct urb *urb)
+{
+	struct usb_stream *s = urb->context;
+	if (balance_playback(urb))
+		stream_start(s, s->i_urb, urb);
+}
+
+int usb_stream_start(struct usb_stream *s)
+{
+	int u, err;
+	int try = 0;
+
+	if (s->state != usb_stream_stopped)
+		return -EAGAIN;
+
+	subs_set_complete(s->inurb, i_capture_start);
+	subs_set_complete(s->outurb, i_playback_start);
+	memset(s->write_page, 0, s->write_size);
+dotry:
+	s->insize_done = 0;
+	s->idle_insize = 0;
+	s->idle_outsize = 0;
+	s->sync_packet = -1;
+	s->iso_frame_balance = 0;
+	++try;
+	for (u = 0; u < 2; u++) {
+		playback_prep_freqn(s, s->outurb[u]);
+		s->inurb[u]->number_of_packets = s->outurb[u]->number_of_packets;
+		s->inurb[u]->transfer_buffer_length =
+			s->inurb[u]->number_of_packets *
+			s->inurb[u]->iso_frame_desc[0].length;
+		preempt_disable();
+		if ((err = usb_submit_urb(s->inurb[u], GFP_ATOMIC)) < 0) {
+			preempt_enable();
+			printk(KERN_ERR "%s: usb_submit_urb(s->inurb[%i])"
+			       " returned %i\n", __FUNCTION__, u, err);
+			return err;
+		}
+
+		if ((err = usb_submit_urb(s->outurb[u], GFP_ATOMIC)) < 0) {
+			preempt_enable();
+			printk(KERN_ERR "%s: usb_submit_urb(s->outurb[%i])"
+			       " returned %i\n", __FUNCTION__, u, err);
+			return err;
+		}
+		preempt_enable();
+		if (s->inurb[u]->start_frame != s->outurb[u]->start_frame)
+			goto check_retry;
+	}
+	try = 0;
+check_retry:
+	if (try) {
+		usb_stream_stop(s);
+		if (try < 5) {
+			printk(KERN_DEBUG"%s:%i: goto dotry;\n",
+			       __FUNCTION__, __LINE__);
+			msleep(500);
+			goto dotry;
+		}
+		printk(KERN_ERR"%s: couldn't start all urbs"
+		       " on the same start_frame.\n", __FUNCTION__);
+		return -EFAULT;
+	}
+
+	s->idle_inurb = s->inurb[USB_STREAM_NURBS - 2];
+	s->idle_outurb = s->outurb[USB_STREAM_NURBS - 2];
+	s->completed_inurb = s->inurb[USB_STREAM_NURBS - 1];
+	s->completed_outurb = s->outurb[USB_STREAM_NURBS - 1];
+
+/* wait, check */
+	{
+		int wait_ms = 3000;
+		while (s->state != usb_stream_ready && wait_ms > 0) {
+			msleep(200);
+			wait_ms -= 200;
+		}
+	}
+
+	return s->state == usb_stream_ready ? 0 : -EFAULT;
+}
+
+
+/*                             stop                                   */
+
+void usb_stream_stop(struct usb_stream *s)
+{
+	int u;
+	if (!s)
+		return;
+	for (u = 0; u < USB_STREAM_NURBS; ++u) {
+		usb_kill_urb(s->inurb[u]);
+		usb_kill_urb(s->outurb[u]);
+	}
+	s->state = usb_stream_stopped;
+}
diff --git a/sound/usb/usx2y/usb_stream.h b/sound/usb/usx2y/usb_stream.h
new file mode 100644
index 0000000..a6f75aa
--- /dev/null
+++ b/sound/usb/usx2y/usb_stream.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2007 by Karsten Wiese <fzu@wemgehoertderstaat.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define USB_STREAM_INTERFACE_VERSION 1
+
+#if !__KERNEL__
+struct usb_iso_packet_descriptor {
+	unsigned int offset;
+	unsigned int length;		/* expected length */
+	unsigned int actual_length;
+	int status;
+};
+
+typedef int wait_queue_head_t;
+
+#endif
+
+#define USB_STREAM_NURBS 4
+struct usb_stream_config {
+	unsigned version;
+	unsigned sample_rate;
+	unsigned period_frames;
+	unsigned frame_size;
+};
+
+struct usb_stream {
+	struct usb_stream_config cfg;
+	unsigned long read_kernel_address;
+	unsigned read_size;
+	unsigned write_size;
+	void *write_page;
+	unsigned long write_kernel_address;
+	unsigned urb_number_of_packets_offset;
+	unsigned urb_iso_frame_desc_offset;
+	unsigned urb_transfer_buffer_offset;
+
+	struct urb *inurb[USB_STREAM_NURBS];
+	struct urb *idle_inurb;
+	struct urb *completed_inurb;
+	struct urb *outurb[USB_STREAM_NURBS];
+	struct urb *idle_outurb;
+	struct urb *completed_outurb;
+	struct urb *i_urb;
+
+	int period_size;
+	int iso_frame_balance;
+	unsigned max_packs;
+
+	unsigned state;
+
+	unsigned out_phase;
+	unsigned out_phase_peeked;
+	unsigned freqn;
+
+/*
+  Consumer info; Userspace has to offset pointers
+*/
+	void *playback1st_to;
+	unsigned playback1st_size;
+	void *playback_to;
+
+	int insplit_pack;
+	int insplit_offset;
+	int next_insplit_pack;
+	int next_insplit_offset;
+	int idle_insize;
+	int idle_outsize;
+	int sync_packet;
+	unsigned insize_done;
+	unsigned periods_done;
+	unsigned periods_polled;
+	wait_queue_head_t sleep;
+
+};
+
+enum usb_stream_state {
+	usb_stream_invalid,
+	usb_stream_stopped,
+	usb_stream_sync0,
+	usb_stream_sync1,
+	usb_stream_ready,
+	usb_stream_running,
+	usb_stream_xrun,
+};
+
+#if __KERNEL__
+
+struct usb_stream *usb_stream_new(struct usb_device *dev,
+				  unsigned in_endpoint,unsigned out_endpoint,
+				  unsigned sample_rate,
+				  unsigned period_frames, unsigned frame_size);
+void usb_stream_free(struct usb_stream *);
+int usb_stream_start(struct usb_stream *);
+void usb_stream_stop(struct usb_stream *);
+
+#endif
-- 
1.5.3.3


[-- Attachment #5: Type: text/plain, Size: 160 bytes --]

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

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

* Re: [PATCH] US-122L Driver, v0
  2007-11-07 18:55 ` [PATCH] US-122L Driver, v0 Karsten Wiese
@ 2007-11-07 19:36   ` Karsten Wiese
  2007-11-08  7:24   ` Clemens Ladisch
  1 sibling, 0 replies; 7+ messages in thread
From: Karsten Wiese @ 2007-11-07 19:36 UTC (permalink / raw)
  To: alsa-devel

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

Am Mittwoch, 7. November 2007 schrieb Karsten Wiese:
> Hi,
> 
> attached patches implement a simple driver for the tascam us-122l.
> Everything except midi out works i think ;-)
> 
> For USB 2.0 you also have to aply
> 	http://marc.info/?l=linux-usb-devel&m=119444869917392&w=2
> named "ehci-hcd: complete iso urbs ASAP ..."
> 
> Will post a jack driver to use it soon.

Attached. It has to be cleaned up alot, but it works.
Patch taken against jackd 0.103.


[-- Attachment #2: jackd-usb_stream-driver.diff.bz2 --]
[-- Type: application/x-bzip2, Size: 14204 bytes --]

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

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

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

* Re: [PATCH] US-122L Driver, v0
  2007-11-07 18:55 ` [PATCH] US-122L Driver, v0 Karsten Wiese
  2007-11-07 19:36   ` Karsten Wiese
@ 2007-11-08  7:24   ` Clemens Ladisch
  2007-11-08 12:37     ` Karsten Wiese
  1 sibling, 1 reply; 7+ messages in thread
From: Clemens Ladisch @ 2007-11-08  7:24 UTC (permalink / raw)
  To: Karsten Wiese, alsa-devel

Karsten Wiese wrote:
> attached patches implement a simple driver for the tascam us-122l.

>  	QUIRK_AUDIO_EDIROL_UA700_UA25,
>  	QUIRK_AUDIO_EDIROL_UA1000,
>  	QUIRK_AUDIO_EDIROL_UA101,
> -
> +	QUIRK_MIDI_US122L,
>  	QUIRK_TYPE_COUNT
>  };
 
Why not put it together with the other QUIRK_MIDI_ symbols?

>  		break;
> +	case QUIRK_MIDI_US122L:
> +		umidi->usb_protocol_ops = &snd_usbmidi_122l_ops;
>  	case QUIRK_MIDI_FIXED_ENDPOINT:
>  		memcpy(&endpoints[0], quirk->data,
>  		       sizeof(struct snd_usb_midi_endpoint_info));

Aaargh!  If you really want to do it this way, at least add a
"/* fall through */" so that it's obvious that this is not a mistake.


Regards,
Clemens

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

* Re: [PATCH] US-122L Driver, v0
  2007-11-08  7:24   ` Clemens Ladisch
@ 2007-11-08 12:37     ` Karsten Wiese
  2007-11-08 16:14       ` Clemens Ladisch
  0 siblings, 1 reply; 7+ messages in thread
From: Karsten Wiese @ 2007-11-08 12:37 UTC (permalink / raw)
  To: Clemens Ladisch; +Cc: alsa-devel

Am Donnerstag, 8. November 2007 schrieb Clemens Ladisch:
>  
> Why not put it together with the other QUIRK_MIDI_ symbols?
> 
> >  		break;
> > +	case QUIRK_MIDI_US122L:
> > +		umidi->usb_protocol_ops = &snd_usbmidi_122l_ops;
> >  	case QUIRK_MIDI_FIXED_ENDPOINT:
> >  		memcpy(&endpoints[0], quirk->data,
> >  		       sizeof(struct snd_usb_midi_endpoint_info));
> 
> Aaargh!  If you really want to do it this way, at least add a
> "/* fall through */" so that it's obvious that this is not a mistake.

Done here, will post again.

I'd like you to try the
  "ehci-hcd: complete iso urbs ASAP for number_of_packets != (n * 8) also"
  http://marc.info/?l=linux-usb-devel&m=119444869917392&w=2
with snd-usb-audio.

snd-usb-audio uses number_of_packets equalling multiples of 8 currently
when transfering over USB 2.0, no?
If so, with above patch that "multiples of 8"-thing could go away and
snd-usb-audio could achieve up to 8 times less userspace-activation-jitter
in low latency setups like 128 frames/period, 2 periods.

thanks,
      Karsten

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

* Re: [PATCH] US-122L Driver, v0
  2007-11-08 12:37     ` Karsten Wiese
@ 2007-11-08 16:14       ` Clemens Ladisch
  2007-11-08 16:42         ` Karsten Wiese
  0 siblings, 1 reply; 7+ messages in thread
From: Clemens Ladisch @ 2007-11-08 16:14 UTC (permalink / raw)
  To: Karsten Wiese; +Cc: alsa-devel

Karsten Wiese wrote:
> snd-usb-audio uses number_of_packets equalling multiples of 8 currently
> when transfering over USB 2.0, no?
> If so, with above patch that "multiples of 8"-thing could go away and
> snd-usb-audio could achieve up to 8 times less userspace-activation-jitter
> in low latency setups like 128 frames/period, 2 periods.

I'm using the 8-microframes alignment because ehci-hcd configures the
controller to interrupt at most every 1 ms.  It has a module option to
change this, but as long as snd-usb-audio cannot detect a lower setting,
it would not make sense to allow shorter periods.


Regards,
Clemens

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

* Re: [PATCH] US-122L Driver, v0
  2007-11-08 16:14       ` Clemens Ladisch
@ 2007-11-08 16:42         ` Karsten Wiese
  0 siblings, 0 replies; 7+ messages in thread
From: Karsten Wiese @ 2007-11-08 16:42 UTC (permalink / raw)
  To: Clemens Ladisch; +Cc: alsa-devel

Am Donnerstag, 8. November 2007 schrieb Clemens Ladisch:
> 
> I'm using the 8-microframes alignment because ehci-hcd configures the
> controller to interrupt at most every 1 ms.  It has a module option to
> change this, but as long as snd-usb-audio cannot detect a lower setting,
> it would not make sense to allow shorter periods.

For an 1ms interrupt alignment ehci-hcd must be loaded with non-default
parameters.
In ehci-hcd.c as of 2.6.23:

/* Initial IRQ latency:  faster than hw default */
static int log2_irq_thresh = 0;		// 0 to 6
module_param (log2_irq_thresh, int, S_IRUGO);
MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes");

That makes a default IRQ latency of 1/8ms. This is also what I see here:
The us122l driver works with 64frames/period 2 periods at 96kHz sample rate.
Thats about 1400 interupts/s. Wouldn't be possible with 1ms alignment.

kind regards,
      Karsten

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

end of thread, other threads:[~2007-11-08 16:42 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-11-07 15:41 Fwd: [PATCH] ehci-hcd: complete iso urbs ASAP for number_of_packets != (n * 8) also Karsten Wiese
2007-11-07 18:55 ` [PATCH] US-122L Driver, v0 Karsten Wiese
2007-11-07 19:36   ` Karsten Wiese
2007-11-08  7:24   ` Clemens Ladisch
2007-11-08 12:37     ` Karsten Wiese
2007-11-08 16:14       ` Clemens Ladisch
2007-11-08 16:42         ` Karsten Wiese

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.