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 #ifdef CONFIG_SND_DEBUG 17 18 #ifdef CONFIG_SND_DEBUG_VERBOSE 19 #define DEFAULT_DEBUG_LEVEL 2 20 #else 21 #define DEFAULT_DEBUG_LEVEL 1 22 #endif 23 24 static int debug = DEFAULT_DEBUG_LEVEL; 25 module_param(debug, int, 0644); 26 MODULE_PARM_DESC(debug, "Debug level (0 = disable)"); 27 28 #endif /* CONFIG_SND_DEBUG */ 29 30 void release_and_free_resource(struct resource *res) 31 { 32 if (res) { 33 release_resource(res); 34 kfree(res); 35 } 36 } 37 EXPORT_SYMBOL(release_and_free_resource); 38 39 #ifdef CONFIG_SND_VERBOSE_PRINTK 40 /* strip the leading path if the given path is absolute */ 41 static const char *sanity_file_name(const char *path) 42 { 43 if (*path == '/') 44 return strrchr(path, '/') + 1; 45 else 46 return path; 47 } 48 #endif 49 50 #if defined(CONFIG_SND_DEBUG) || defined(CONFIG_SND_VERBOSE_PRINTK) 51 void __snd_printk(unsigned int level, const char *path, int line, 52 const char *format, ...) 53 { 54 va_list args; 55 #ifdef CONFIG_SND_VERBOSE_PRINTK 56 int kern_level; 57 struct va_format vaf; 58 char verbose_fmt[] = KERN_DEFAULT "ALSA %s:%d %pV"; 59 bool level_found = false; 60 #endif 61 62 #ifdef CONFIG_SND_DEBUG 63 if (debug < level) 64 return; 65 #endif 66 67 va_start(args, format); 68 #ifdef CONFIG_SND_VERBOSE_PRINTK 69 vaf.fmt = format; 70 vaf.va = &args; 71 72 while ((kern_level = printk_get_level(vaf.fmt)) != 0) { 73 const char *end_of_header = printk_skip_level(vaf.fmt); 74 75 /* Ignore KERN_CONT. We print filename:line for each piece. */ 76 if (kern_level >= '0' && kern_level <= '7') { 77 memcpy(verbose_fmt, vaf.fmt, end_of_header - vaf.fmt); 78 level_found = true; 79 } 80 81 vaf.fmt = end_of_header; 82 } 83 84 if (!level_found && level) 85 memcpy(verbose_fmt, KERN_DEBUG, sizeof(KERN_DEBUG) - 1); 86 87 printk(verbose_fmt, sanity_file_name(path), line, &vaf); 88 #else 89 vprintk(format, args); 90 #endif 91 va_end(args); 92 } 93 EXPORT_SYMBOL_GPL(__snd_printk); 94 #endif 95 96 #ifdef CONFIG_PCI 97 #include <linux/pci.h> 98 /** 99 * snd_pci_quirk_lookup_id - look up a PCI SSID quirk list 100 * @vendor: PCI SSV id 101 * @device: PCI SSD id 102 * @list: quirk list, terminated by a null entry 103 * 104 * Look through the given quirk list and finds a matching entry 105 * with the same PCI SSID. When subdevice is 0, all subdevice 106 * values may match. 107 * 108 * Returns the matched entry pointer, or NULL if nothing matched. 109 */ 110 const struct snd_pci_quirk * 111 snd_pci_quirk_lookup_id(u16 vendor, u16 device, 112 const struct snd_pci_quirk *list) 113 { 114 const struct snd_pci_quirk *q; 115 116 for (q = list; q->subvendor || q->subdevice; q++) { 117 if (q->subvendor != vendor) 118 continue; 119 if (!q->subdevice || 120 (device & q->subdevice_mask) == q->subdevice) 121 return q; 122 } 123 return NULL; 124 } 125 EXPORT_SYMBOL(snd_pci_quirk_lookup_id); 126 127 /** 128 * snd_pci_quirk_lookup - look up a PCI SSID quirk list 129 * @pci: pci_dev handle 130 * @list: quirk list, terminated by a null entry 131 * 132 * Look through the given quirk list and finds a matching entry 133 * with the same PCI SSID. When subdevice is 0, all subdevice 134 * values may match. 135 * 136 * Returns the matched entry pointer, or NULL if nothing matched. 137 */ 138 const struct snd_pci_quirk * 139 snd_pci_quirk_lookup(struct pci_dev *pci, const struct snd_pci_quirk *list) 140 { 141 if (!pci) 142 return NULL; 143 return snd_pci_quirk_lookup_id(pci->subsystem_vendor, 144 pci->subsystem_device, 145 list); 146 } 147 EXPORT_SYMBOL(snd_pci_quirk_lookup); 148 #endif 149 150 /* 151 * Deferred async signal helpers 152 * 153 * Below are a few helper functions to wrap the async signal handling 154 * in the deferred work. The main purpose is to avoid the messy deadlock 155 * around tasklist_lock and co at the kill_fasync() invocation. 156 * fasync_helper() and kill_fasync() are replaced with snd_fasync_helper() 157 * and snd_kill_fasync(), respectively. In addition, snd_fasync_free() has 158 * to be called at releasing the relevant file object. 159 */ 160 struct snd_fasync { 161 struct fasync_struct *fasync; 162 int signal; 163 int poll; 164 int on; 165 struct list_head list; 166 }; 167 168 static DEFINE_SPINLOCK(snd_fasync_lock); 169 static LIST_HEAD(snd_fasync_list); 170 171 static void snd_fasync_work_fn(struct work_struct *work) 172 { 173 struct snd_fasync *fasync; 174 175 spin_lock_irq(&snd_fasync_lock); 176 while (!list_empty(&snd_fasync_list)) { 177 fasync = list_first_entry(&snd_fasync_list, struct snd_fasync, list); 178 list_del_init(&fasync->list); 179 spin_unlock_irq(&snd_fasync_lock); 180 if (fasync->on) 181 kill_fasync(&fasync->fasync, fasync->signal, fasync->poll); 182 spin_lock_irq(&snd_fasync_lock); 183 } 184 spin_unlock_irq(&snd_fasync_lock); 185 } 186 187 static DECLARE_WORK(snd_fasync_work, snd_fasync_work_fn); 188 189 int snd_fasync_helper(int fd, struct file *file, int on, 190 struct snd_fasync **fasyncp) 191 { 192 struct snd_fasync *fasync = NULL; 193 194 if (on) { 195 fasync = kzalloc(sizeof(*fasync), GFP_KERNEL); 196 if (!fasync) 197 return -ENOMEM; 198 INIT_LIST_HEAD(&fasync->list); 199 } 200 201 spin_lock_irq(&snd_fasync_lock); 202 if (*fasyncp) { 203 kfree(fasync); 204 fasync = *fasyncp; 205 } else { 206 if (!fasync) { 207 spin_unlock_irq(&snd_fasync_lock); 208 return 0; 209 } 210 *fasyncp = fasync; 211 } 212 fasync->on = on; 213 spin_unlock_irq(&snd_fasync_lock); 214 return fasync_helper(fd, file, on, &fasync->fasync); 215 } 216 EXPORT_SYMBOL_GPL(snd_fasync_helper); 217 218 void snd_kill_fasync(struct snd_fasync *fasync, int signal, int poll) 219 { 220 unsigned long flags; 221 222 if (!fasync || !fasync->on) 223 return; 224 spin_lock_irqsave(&snd_fasync_lock, flags); 225 fasync->signal = signal; 226 fasync->poll = poll; 227 list_move(&fasync->list, &snd_fasync_list); 228 schedule_work(&snd_fasync_work); 229 spin_unlock_irqrestore(&snd_fasync_lock, flags); 230 } 231 EXPORT_SYMBOL_GPL(snd_kill_fasync); 232 233 void snd_fasync_free(struct snd_fasync *fasync) 234 { 235 if (!fasync) 236 return; 237 fasync->on = 0; 238 flush_work(&snd_fasync_work); 239 kfree(fasync); 240 } 241 EXPORT_SYMBOL_GPL(snd_fasync_free); 242