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 */

Reply via email to