At Mon, 24 Nov 2003 13:51:50 +0100,
I wrote:
>
> At Mon, 24 Nov 2003 13:36:42 +0100,
> I wrote:
> >
> > meanwhile, i'm trying to implement:
> >
> > (4) allow prepare callback to sleep with a special flag.
> >
> > this will be useful for other drivers, too, such as vx and korg1212
> > drivers which require the handshaking.
> > but what i'm doing is still a hack, and will be a fundamental rewrite
> > later.
>
> the attached is a quick-hacked version.
> it's untested for linked streams. we need a test case here.
ok, here is the final version. this one seems working fine.
Jaroslav, could you check whether it's ok?
the changes in pcm_native.c shouldn't affect other cards.
Takashi
Index: alsa-kernel/core/pcm_native.c
===================================================================
RCS file: /suse/tiwai/cvs/alsa/alsa-kernel/core/pcm_native.c,v
retrieving revision 1.63
diff -u -r1.63 pcm_native.c
--- alsa-kernel/core/pcm_native.c 21 Oct 2003 09:49:34 -0000 1.63
+++ alsa-kernel/core/pcm_native.c 24 Nov 2003 17:46:04 -0000
@@ -620,7 +620,7 @@
*/
static int snd_pcm_action_group(struct action_ops *ops,
snd_pcm_substream_t *substream,
- int state)
+ int state, int atomic_only)
{
struct list_head *pos;
snd_pcm_substream_t *s = NULL;
@@ -628,6 +628,8 @@
snd_pcm_group_for_each(pos, substream) {
s = snd_pcm_group_substream_entry(pos);
+ if (atomic_only && (s->pcm->info_flags & SNDRV_PCM_INFO_NONATOMIC_OPS))
+ continue;
if (s != substream)
spin_lock(&s->self_group.lock);
res = ops->pre_action(s, state);
@@ -637,6 +639,8 @@
if (res >= 0) {
snd_pcm_group_for_each(pos, substream) {
s = snd_pcm_group_substream_entry(pos);
+ if (atomic_only && (s->pcm->info_flags &
SNDRV_PCM_INFO_NONATOMIC_OPS))
+ continue;
err = ops->do_action(s, state);
if (err < 0) {
if (res == 0)
@@ -652,7 +656,9 @@
/* unlock all streams */
snd_pcm_group_for_each(pos, substream) {
s1 = snd_pcm_group_substream_entry(pos);
- if (s1 != substream)
+ if (atomic_only && (s1->pcm->info_flags &
SNDRV_PCM_INFO_NONATOMIC_OPS))
+ ;
+ else if (s1 != substream)
spin_unlock(&s1->self_group.lock);
if (s1 == s) /* end */
break;
@@ -682,6 +688,8 @@
/*
* Note: call with stream lock
+ *
+ * NB2: this won't handle the non-atomic callbacks
*/
static int snd_pcm_action(struct action_ops *ops,
snd_pcm_substream_t *substream,
@@ -695,7 +703,7 @@
spin_lock(&substream->group->lock);
spin_lock(&substream->self_group.lock);
}
- res = snd_pcm_action_group(ops, substream, state);
+ res = snd_pcm_action_group(ops, substream, state, 0);
spin_unlock(&substream->group->lock);
} else {
res = snd_pcm_action_single(ops, substream, state);
@@ -705,10 +713,14 @@
/*
* Note: don't use any locks before
+ *
+ * NB2: this can handle the non-atomic callbacks if allow_nonatomic = 1
+ * when the pcm->info_flags has NONATOMIC_OPS bit, it's handled
+ * ouside the lock to allow sleep in the callback.
*/
static int snd_pcm_action_lock_irq(struct action_ops *ops,
snd_pcm_substream_t *substream,
- int state)
+ int state, int allow_nonatomic)
{
int res;
@@ -716,10 +728,43 @@
if (snd_pcm_stream_linked(substream)) {
spin_lock(&substream->group->lock);
spin_lock(&substream->self_group.lock);
- res = snd_pcm_action_group(ops, substream, state);
+ res = snd_pcm_action_group(ops, substream, state, allow_nonatomic);
spin_unlock(&substream->self_group.lock);
spin_unlock(&substream->group->lock);
+ if (res >= 0 && allow_nonatomic) {
+ /* now process the non-atomic substreams separately
+ * outside the lock
+ */
+#define MAX_LINKED_STREAMS 16 /* FIXME: should be variable */
+
+ struct list_head *pos;
+ int i, num_s = 0;
+ snd_pcm_substream_t *s;
+ snd_pcm_substream_t *subs[MAX_LINKED_STREAMS];
+ snd_pcm_group_for_each(pos, substream) {
+ if (num_s >= MAX_LINKED_STREAMS) {
+ res = -ENOMEM;
+ num_s = 0; /* don't proceed */
+ break;
+ }
+ s = snd_pcm_group_substream_entry(pos);
+ if (s->pcm->info_flags & SNDRV_PCM_INFO_NONATOMIC_OPS)
+ subs[num_s++] = s;
+ }
+ if (num_s > 0) {
+ read_unlock_irq(&snd_pcm_link_rwlock);
+ for (i = 0; i < num_s && res >= 0; i++)
+ res = snd_pcm_action_single(ops, subs[i],
state);
+ return res;
+ }
+ }
} else {
+ if (allow_nonatomic &&
+ (substream->pcm->info_flags & SNDRV_PCM_INFO_NONATOMIC_OPS)) {
+ read_unlock_irq(&snd_pcm_link_rwlock);
+ /* process outside the lock */
+ return snd_pcm_action_single(ops, substream, state);
+ }
spin_lock(&substream->self_group.lock);
res = snd_pcm_action_single(ops, substream, state);
spin_unlock(&substream->self_group.lock);
@@ -993,7 +1038,7 @@
snd_power_lock(card);
if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile)) >= 0)
- return snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream, 0);
+ return snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream, 0,
0);
snd_power_unlock(card);
return res;
}
@@ -1083,7 +1128,7 @@
static int snd_pcm_reset(snd_pcm_substream_t *substream)
{
- return snd_pcm_action_lock_irq(&snd_pcm_action_reset, substream, 0);
+ return snd_pcm_action_lock_irq(&snd_pcm_action_reset, substream, 0, 0);
}
static int snd_pcm_pre_prepare(snd_pcm_substream_t * substream, int state)
@@ -1131,7 +1176,7 @@
snd_power_lock(card);
if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile)) >= 0)
- res = snd_pcm_action_lock_irq(&snd_pcm_action_prepare, substream, 0);
+ res = snd_pcm_action_lock_irq(&snd_pcm_action_prepare, substream, 0,
1); /* allow sleep if specified */
snd_power_unlock(card);
return res;
}
@@ -2330,7 +2375,7 @@
case SNDRV_PCM_IOCTL_RESET:
return snd_pcm_reset(substream);
case SNDRV_PCM_IOCTL_START:
- return snd_pcm_action(&snd_pcm_action_start, substream, 0);
+ return snd_pcm_action_lock_irq(&snd_pcm_action_start, substream, 0, 0);
case SNDRV_PCM_IOCTL_LINK:
return snd_pcm_link(substream, (long) arg);
case SNDRV_PCM_IOCTL_UNLINK:
Index: alsa-kernel/include/asound.h
===================================================================
RCS file: /suse/tiwai/cvs/alsa/alsa-kernel/include/asound.h,v
retrieving revision 1.24
diff -u -r1.24 asound.h
--- alsa-kernel/include/asound.h 22 Oct 2003 09:41:06 -0000 1.24
+++ alsa-kernel/include/asound.h 24 Nov 2003 12:23:47 -0000
@@ -272,6 +272,7 @@
#define SNDRV_PCM_INFO_HALF_DUPLEX 0x00100000 /* only half duplex */
#define SNDRV_PCM_INFO_JOINT_DUPLEX 0x00200000 /* playback and capture stream
are somewhat correlated */
#define SNDRV_PCM_INFO_SYNC_START 0x00400000 /* pcm support some kind of
sync go */
+#define SNDRV_PCM_INFO_NONATOMIC_OPS 0x00800000 /* non-atomic prepare callback
*/
enum sndrv_pcm_state {
SNDRV_PCM_STATE_OPEN = 0, /* stream is open */
Index: alsa-kernel/usb/usbaudio.c
===================================================================
RCS file: /suse/tiwai/cvs/alsa/alsa-kernel/usb/usbaudio.c,v
retrieving revision 1.77
diff -u -r1.77 usbaudio.c
--- alsa-kernel/usb/usbaudio.c 24 Nov 2003 11:51:02 -0000 1.77
+++ alsa-kernel/usb/usbaudio.c 24 Nov 2003 17:49:59 -0000
@@ -28,9 +28,8 @@
* NOTES:
*
* - async unlink should be used for avoiding the sleep inside lock.
- * however, it causes oops by unknown reason on usb-uhci, and
- * disabled as default. the feature is enabled by async_unlink=1
- * option (especially when preempt is used).
+ * 2.4.22 usb-uhci seems buggy for async unlinking and results in
+ * oops. in such a cse, pass async_unlink=0 option.
* - the linked URBs would be preferred but not used so far because of
* the instability of unlinking.
* - type II is not supported properly. there is no device which supports
@@ -69,7 +68,7 @@
static int vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Vendor ID for this
card */
static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Product ID for
this card */
static int nrpacks = 4; /* max. number of packets per urb */
-static int async_unlink = 0;
+static int async_unlink = 1;
MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
MODULE_PARM_DESC(index, "Index value for the USB audio adapter.");
@@ -804,8 +803,7 @@
int i;
/* stop urbs (to be sure) */
- deactivate_urbs(subs, force, 1);
- if (async_unlink)
+ if (deactivate_urbs(subs, force, 1) > 0)
wait_clear_urbs(subs);
for (i = 0; i < MAX_URBS; i++)
@@ -834,12 +832,6 @@
subs->freqmax = subs->freqn + (subs->freqn >> 2); /* max. allowed frequency */
subs->phase = 0;
- /* reset the pointer */
- subs->hwptr = 0;
- subs->hwptr_done = 0;
- subs->transfer_sched = 0;
- subs->transfer_done = 0;
-
/* calculate the max. size of packet */
maxsize = ((subs->freqmax + 0x3fff) * (frame_bits >> 3)) >> 14;
if (subs->maxpacksize && maxsize > subs->maxpacksize) {
@@ -1277,6 +1269,17 @@
subs->maxframesize = bytes_to_frames(runtime, subs->maxpacksize);
subs->curframesize = bytes_to_frames(runtime, subs->curpacksize);
+ /* reset the pointer */
+ subs->hwptr = 0;
+ subs->hwptr_done = 0;
+ subs->transfer_sched = 0;
+ subs->transfer_done = 0;
+ subs->phase = 0;
+
+ /* clear urbs (to be sure) */
+ if (deactivate_urbs(subs, 0, 0) > 0)
+ wait_clear_urbs(subs);
+
return 0;
}
@@ -2002,7 +2005,7 @@
as->pcm = pcm;
pcm->private_data = as;
pcm->private_free = snd_usb_audio_pcm_free;
- pcm->info_flags = 0;
+ pcm->info_flags = SNDRV_PCM_INFO_NONATOMIC_OPS;
if (chip->pcm_devs > 0)
sprintf(pcm->name, "USB Audio #%d", chip->pcm_devs);
else
Index: alsa-driver/usb/usbaudio.patch
===================================================================
RCS file: /suse/tiwai/cvs/alsa/alsa-driver/usb/usbaudio.patch,v
retrieving revision 1.1
diff -u -r1.1 usbaudio.patch
--- alsa-driver/usb/usbaudio.patch 2 Jun 2003 10:07:21 -0000 1.1
+++ alsa-driver/usb/usbaudio.patch 24 Nov 2003 17:51:55 -0000
@@ -1,11 +1,25 @@
---- usbaudio.c 2003-06-01 20:32:24.000000000 +0200
-+++ usbaudio.c.old 2003-06-01 20:32:42.000000000 +0200
+--- usbaudio.c 2003-11-24 18:49:59.000000000 +0100
++++ usbaudio.c 2003-11-24 18:51:33.000000000 +0100
@@ -1,3 +1,4 @@
+#include "usbaudio.inc"
/*
* (Tentative) USB Audio Driver for ALSA
*
-@@ -1656,9 +1657,11 @@
+@@ -68,7 +69,12 @@
+ static int vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Vendor ID for
this card */
+ static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Product ID for
this card */
+ static int nrpacks = 4; /* max. number of packets per urb */
+-static int async_unlink = 1;
++static int async_unlink =
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
++1;
++#else
++0; /* disabled as default for buggy async-unlink handling */
++#endif
+
+ MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+ MODULE_PARM_DESC(index, "Index value for the USB audio adapter.");
+@@ -1761,9 +1767,11 @@
* entry point for linux usb interface
*/
@@ -17,7 +31,7 @@
static struct usb_device_id usb_audio_ids [] = {
#include "usbquirks.h"
-@@ -1671,10 +1674,15 @@
+@@ -1776,10 +1784,15 @@
MODULE_DEVICE_TABLE (usb, usb_audio_ids);
static struct usb_driver usb_audio_driver = {
@@ -33,7 +47,7 @@
.id_table = usb_audio_ids,
};
-@@ -2744,6 +2752,7 @@
+@@ -2918,6 +2931,7 @@
}
}
@@ -41,7 +55,7 @@
/*
* new 2.5 USB kernel API
*/
-@@ -2764,6 +2773,8 @@
+@@ -2938,6 +2952,8 @@
snd_usb_audio_disconnect(interface_to_usbdev(intf),
dev_get_drvdata(&intf->dev));
}
@@ -50,7 +64,7 @@
static int __init snd_usb_audio_init(void)
-@@ -2803,3 +2814,5 @@
+@@ -2981,3 +2997,5 @@
__setup("snd-usb-audio=", snd_usb_audio_module_setup);
#endif /* !MODULE */