ALSA: control: Drop superfluous ifdef CONFIG_SND_CTL_DEBUG
[linux-2.6-microblaze.git] / sound / core / control.c
index a25c0d6..5593988 100644 (file)
@@ -364,6 +364,93 @@ static int snd_ctl_find_hole(struct snd_card *card, unsigned int count)
        return 0;
 }
 
+/* check whether the given id is contained in the given kctl */
+static bool elem_id_matches(const struct snd_kcontrol *kctl,
+                           const struct snd_ctl_elem_id *id)
+{
+       return kctl->id.iface == id->iface &&
+               kctl->id.device == id->device &&
+               kctl->id.subdevice == id->subdevice &&
+               !strncmp(kctl->id.name, id->name, sizeof(kctl->id.name)) &&
+               kctl->id.index <= id->index &&
+               kctl->id.index + kctl->count > id->index;
+}
+
+#ifdef CONFIG_SND_CTL_FAST_LOOKUP
+/* Compute a hash key for the corresponding ctl id
+ * It's for the name lookup, hence the numid is excluded.
+ * The hash key is bound in LONG_MAX to be used for Xarray key.
+ */
+#define MULTIPLIER     37
+static unsigned long get_ctl_id_hash(const struct snd_ctl_elem_id *id)
+{
+       unsigned long h;
+       const unsigned char *p;
+
+       h = id->iface;
+       h = MULTIPLIER * h + id->device;
+       h = MULTIPLIER * h + id->subdevice;
+       for (p = id->name; *p; p++)
+               h = MULTIPLIER * h + *p;
+       h = MULTIPLIER * h + id->index;
+       h &= LONG_MAX;
+       return h;
+}
+
+/* add hash entries to numid and ctl xarray tables */
+static void add_hash_entries(struct snd_card *card,
+                            struct snd_kcontrol *kcontrol)
+{
+       struct snd_ctl_elem_id id = kcontrol->id;
+       int i;
+
+       xa_store_range(&card->ctl_numids, kcontrol->id.numid,
+                      kcontrol->id.numid + kcontrol->count - 1,
+                      kcontrol, GFP_KERNEL);
+
+       for (i = 0; i < kcontrol->count; i++) {
+               id.index = kcontrol->id.index + i;
+               if (xa_insert(&card->ctl_hash, get_ctl_id_hash(&id),
+                             kcontrol, GFP_KERNEL)) {
+                       /* skip hash for this entry, noting we had collision */
+                       card->ctl_hash_collision = true;
+                       dev_dbg(card->dev, "ctl_hash collision %d:%s:%d\n",
+                               id.iface, id.name, id.index);
+               }
+       }
+}
+
+/* remove hash entries that have been added */
+static void remove_hash_entries(struct snd_card *card,
+                               struct snd_kcontrol *kcontrol)
+{
+       struct snd_ctl_elem_id id = kcontrol->id;
+       struct snd_kcontrol *matched;
+       unsigned long h;
+       int i;
+
+       for (i = 0; i < kcontrol->count; i++) {
+               xa_erase(&card->ctl_numids, id.numid);
+               h = get_ctl_id_hash(&id);
+               matched = xa_load(&card->ctl_hash, h);
+               if (matched && (matched == kcontrol ||
+                               elem_id_matches(matched, &id)))
+                       xa_erase(&card->ctl_hash, h);
+               id.index++;
+               id.numid++;
+       }
+}
+#else /* CONFIG_SND_CTL_FAST_LOOKUP */
+static inline void add_hash_entries(struct snd_card *card,
+                                   struct snd_kcontrol *kcontrol)
+{
+}
+static inline void remove_hash_entries(struct snd_card *card,
+                                      struct snd_kcontrol *kcontrol)
+{
+}
+#endif /* CONFIG_SND_CTL_FAST_LOOKUP */
+
 enum snd_ctl_add_mode {
        CTL_ADD_EXCLUSIVE, CTL_REPLACE, CTL_ADD_ON_REPLACE,
 };
@@ -408,6 +495,8 @@ static int __snd_ctl_add_replace(struct snd_card *card,
        kcontrol->id.numid = card->last_numid + 1;
        card->last_numid += kcontrol->count;
 
+       add_hash_entries(card, kcontrol);
+
        for (idx = 0; idx < kcontrol->count; idx++)
                snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_ADD, kcontrol, idx);
 
@@ -479,6 +568,26 @@ int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL(snd_ctl_replace);
 
+static int __snd_ctl_remove(struct snd_card *card,
+                           struct snd_kcontrol *kcontrol,
+                           bool remove_hash)
+{
+       unsigned int idx;
+
+       if (snd_BUG_ON(!card || !kcontrol))
+               return -EINVAL;
+       list_del(&kcontrol->list);
+
+       if (remove_hash)
+               remove_hash_entries(card, kcontrol);
+
+       card->controls_count -= kcontrol->count;
+       for (idx = 0; idx < kcontrol->count; idx++)
+               snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_REMOVE, kcontrol, idx);
+       snd_ctl_free_one(kcontrol);
+       return 0;
+}
+
 /**
  * snd_ctl_remove - remove the control from the card and release it
  * @card: the card instance
@@ -492,16 +601,7 @@ EXPORT_SYMBOL(snd_ctl_replace);
  */
 int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol)
 {
-       unsigned int idx;
-
-       if (snd_BUG_ON(!card || !kcontrol))
-               return -EINVAL;
-       list_del(&kcontrol->list);
-       card->controls_count -= kcontrol->count;
-       for (idx = 0; idx < kcontrol->count; idx++)
-               snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_REMOVE, kcontrol, idx);
-       snd_ctl_free_one(kcontrol);
-       return 0;
+       return __snd_ctl_remove(card, kcontrol, true);
 }
 EXPORT_SYMBOL(snd_ctl_remove);
 
@@ -642,14 +742,30 @@ int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id,
                up_write(&card->controls_rwsem);
                return -ENOENT;
        }
+       remove_hash_entries(card, kctl);
        kctl->id = *dst_id;
        kctl->id.numid = card->last_numid + 1;
        card->last_numid += kctl->count;
+       add_hash_entries(card, kctl);
        up_write(&card->controls_rwsem);
        return 0;
 }
 EXPORT_SYMBOL(snd_ctl_rename_id);
 
+#ifndef CONFIG_SND_CTL_FAST_LOOKUP
+static struct snd_kcontrol *
+snd_ctl_find_numid_slow(struct snd_card *card, unsigned int numid)
+{
+       struct snd_kcontrol *kctl;
+
+       list_for_each_entry(kctl, &card->controls, list) {
+               if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid)
+                       return kctl;
+       }
+       return NULL;
+}
+#endif /* !CONFIG_SND_CTL_FAST_LOOKUP */
+
 /**
  * snd_ctl_find_numid - find the control instance with the given number-id
  * @card: the card instance
@@ -665,15 +781,13 @@ EXPORT_SYMBOL(snd_ctl_rename_id);
  */
 struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numid)
 {
-       struct snd_kcontrol *kctl;
-
        if (snd_BUG_ON(!card || !numid))
                return NULL;
-       list_for_each_entry(kctl, &card->controls, list) {
-               if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid)
-                       return kctl;
-       }
-       return NULL;
+#ifdef CONFIG_SND_CTL_FAST_LOOKUP
+       return xa_load(&card->ctl_numids, numid);
+#else
+       return snd_ctl_find_numid_slow(card, numid);
+#endif
 }
 EXPORT_SYMBOL(snd_ctl_find_numid);
 
@@ -699,21 +813,18 @@ struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
                return NULL;
        if (id->numid != 0)
                return snd_ctl_find_numid(card, id->numid);
-       list_for_each_entry(kctl, &card->controls, list) {
-               if (kctl->id.iface != id->iface)
-                       continue;
-               if (kctl->id.device != id->device)
-                       continue;
-               if (kctl->id.subdevice != id->subdevice)
-                       continue;
-               if (strncmp(kctl->id.name, id->name, sizeof(kctl->id.name)))
-                       continue;
-               if (kctl->id.index > id->index)
-                       continue;
-               if (kctl->id.index + kctl->count <= id->index)
-                       continue;
+#ifdef CONFIG_SND_CTL_FAST_LOOKUP
+       kctl = xa_load(&card->ctl_hash, get_ctl_id_hash(id));
+       if (kctl && elem_id_matches(kctl, id))
                return kctl;
-       }
+       if (!card->ctl_hash_collision)
+               return NULL; /* we can rely on only hash table */
+#endif
+       /* no matching in hash table - try all as the last resort */
+       list_for_each_entry(kctl, &card->controls, list)
+               if (elem_id_matches(kctl, id))
+                       return kctl;
+
        return NULL;
 }
 EXPORT_SYMBOL(snd_ctl_find_id);
@@ -855,7 +966,6 @@ static const unsigned int value_sizes[] = {
        [SNDRV_CTL_ELEM_TYPE_INTEGER64] = sizeof(long long),
 };
 
-#ifdef CONFIG_SND_CTL_VALIDATION
 /* fill the remaining snd_ctl_elem_value data with the given pattern */
 static void fill_remaining_elem_value(struct snd_ctl_elem_value *control,
                                      struct snd_ctl_elem_info *info,
@@ -967,21 +1077,6 @@ static int sanity_check_elem_value(struct snd_card *card,
 
        return ret;
 }
-#else
-static inline void fill_remaining_elem_value(struct snd_ctl_elem_value *control,
-                                            struct snd_ctl_elem_info *info,
-                                            u32 pattern)
-{
-}
-
-static inline int sanity_check_elem_value(struct snd_card *card,
-                                         struct snd_ctl_elem_value *control,
-                                         struct snd_ctl_elem_info *info,
-                                         u32 pattern)
-{
-       return 0;
-}
-#endif
 
 static int __snd_ctl_elem_info(struct snd_card *card,
                               struct snd_kcontrol *kctl,
@@ -1077,7 +1172,7 @@ static int snd_ctl_elem_read(struct snd_card *card,
 
        snd_ctl_build_ioff(&control->id, kctl, index_offset);
 
-#ifdef CONFIG_SND_CTL_VALIDATION
+#ifdef CONFIG_SND_CTL_DEBUG
        /* info is needed only for validation */
        memset(&info, 0, sizeof(info));
        info.id = control->id;
@@ -2195,8 +2290,13 @@ static int snd_ctl_dev_free(struct snd_device *device)
        down_write(&card->controls_rwsem);
        while (!list_empty(&card->controls)) {
                control = snd_kcontrol(card->controls.next);
-               snd_ctl_remove(card, control);
+               __snd_ctl_remove(card, control, false);
        }
+
+#ifdef CONFIG_SND_CTL_FAST_LOOKUP
+       xa_destroy(&card->ctl_numids);
+       xa_destroy(&card->ctl_hash);
+#endif
        up_write(&card->controls_rwsem);
        put_device(&card->ctl_dev);
        return 0;