1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Misc and compatibility things 4 * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 5 */ 6 7 #include <linux/init.h> 8 #include <linux/export.h> 9 #include <linux/moduleparam.h> 10 #include <linux/time.h> 11 #include <linux/slab.h> 12 #include <linux/ioport.h> 13 #include <linux/fs.h> 14 #include <sound/core.h> 15 16 void release_and_free_resource(struct resource *res) 17 { 18 if (res) { 19 release_resource(res); 20 kfree(res); 21 } 22 } 23 EXPORT_SYMBOL(release_and_free_resource); 24 25 #ifdef CONFIG_PCI 26 #include <linux/pci.h> 27 /** 28 * snd_pci_quirk_lookup_id - look up a PCI SSID quirk list 29 * @vendor: PCI SSV id 30 * @device: PCI SSD id 31 * @list: quirk list, terminated by a null entry 32 * 33 * Look through the given quirk list and finds a matching entry 34 * with the same PCI SSID. When subdevice is 0, all subdevice 35 * values may match. 36 * 37 * Returns the matched entry pointer, or NULL if nothing matched. 38 */ 39 const struct snd_pci_quirk * 40 snd_pci_quirk_lookup_id(u16 vendor, u16 device, 41 const struct snd_pci_quirk *list) 42 { 43 const struct snd_pci_quirk *q; 44 45 for (q = list; q->subvendor || q->subdevice; q++) { 46 if (q->subvendor != vendor) 47 continue; 48 if (!q->subdevice || 49 (device & q->subdevice_mask) == q->subdevice) 50 return q; 51 } 52 return NULL; 53 } 54 EXPORT_SYMBOL(snd_pci_quirk_lookup_id); 55 56 /** 57 * snd_pci_quirk_lookup - look up a PCI SSID quirk list 58 * @pci: pci_dev handle 59 * @list: quirk list, terminated by a null entry 60 * 61 * Look through the given quirk list and finds a matching entry 62 * with the same PCI SSID. When subdevice is 0, all subdevice 63 * values may match. 64 * 65 * Returns the matched entry pointer, or NULL if nothing matched. 66 */ 67 const struct snd_pci_quirk * 68 snd_pci_quirk_lookup(struct pci_dev *pci, const struct snd_pci_quirk *list) 69 { 70 if (!pci) 71 return NULL; 72 return snd_pci_quirk_lookup_id(pci->subsystem_vendor, 73 pci->subsystem_device, 74 list); 75 } 76 EXPORT_SYMBOL(snd_pci_quirk_lookup); 77 #endif 78 79 /* 80 * Deferred async signal helpers 81 * 82 * Below are a few helper functions to wrap the async signal handling 83 * in the deferred work. The main purpose is to avoid the messy deadlock 84 * around tasklist_lock and co at the kill_fasync() invocation. 85 * fasync_helper() and kill_fasync() are replaced with snd_fasync_helper() 86 * and snd_kill_fasync(), respectively. In addition, snd_fasync_free() has 87 * to be called at releasing the relevant file object. 88 */ 89 struct snd_fasync { 90 struct fasync_struct *fasync; 91 int signal; 92 int poll; 93 int on; 94 struct list_head list; 95 }; 96 97 static DEFINE_SPINLOCK(snd_fasync_lock); 98 static LIST_HEAD(snd_fasync_list); 99 100 static void snd_fasync_work_fn(struct work_struct *work) 101 { 102 struct snd_fasync *fasync; 103 104 spin_lock_irq(&snd_fasync_lock); 105 while (!list_empty(&snd_fasync_list)) { 106 fasync = list_first_entry(&snd_fasync_list, struct snd_fasync, list); 107 list_del_init(&fasync->list); 108 spin_unlock_irq(&snd_fasync_lock); 109 if (fasync->on) 110 kill_fasync(&fasync->fasync, fasync->signal, fasync->poll); 111 spin_lock_irq(&snd_fasync_lock); 112 } 113 spin_unlock_irq(&snd_fasync_lock); 114 } 115 116 static DECLARE_WORK(snd_fasync_work, snd_fasync_work_fn); 117 118 int snd_fasync_helper(int fd, struct file *file, int on, 119 struct snd_fasync **fasyncp) 120 { 121 struct snd_fasync *fasync = NULL; 122 123 if (on) { 124 fasync = kzalloc(sizeof(*fasync), GFP_KERNEL); 125 if (!fasync) 126 return -ENOMEM; 127 INIT_LIST_HEAD(&fasync->list); 128 } 129 130 spin_lock_irq(&snd_fasync_lock); 131 if (*fasyncp) { 132 kfree(fasync); 133 fasync = *fasyncp; 134 } else { 135 if (!fasync) { 136 spin_unlock_irq(&snd_fasync_lock); 137 return 0; 138 } 139 *fasyncp = fasync; 140 } 141 fasync->on = on; 142 spin_unlock_irq(&snd_fasync_lock); 143 return fasync_helper(fd, file, on, &fasync->fasync); 144 } 145 EXPORT_SYMBOL_GPL(snd_fasync_helper); 146 147 void snd_kill_fasync(struct snd_fasync *fasync, int signal, int poll) 148 { 149 unsigned long flags; 150 151 if (!fasync || !fasync->on) 152 return; 153 spin_lock_irqsave(&snd_fasync_lock, flags); 154 fasync->signal = signal; 155 fasync->poll = poll; 156 list_move(&fasync->list, &snd_fasync_list); 157 schedule_work(&snd_fasync_work); 158 spin_unlock_irqrestore(&snd_fasync_lock, flags); 159 } 160 EXPORT_SYMBOL_GPL(snd_kill_fasync); 161 162 void snd_fasync_free(struct snd_fasync *fasync) 163 { 164 if (!fasync) 165 return; 166 fasync->on = 0; 167 flush_work(&snd_fasync_work); 168 kfree(fasync); 169 } 170 EXPORT_SYMBOL_GPL(snd_fasync_free); 171