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 24static int debug = DEFAULT_DEBUG_LEVEL; 25module_param(debug, int, 0644); 26MODULE_PARM_DESC(debug, "Debug level (0 = disable)"); 27 28#endif /* CONFIG_SND_DEBUG */ 29 30void release_and_free_resource(struct resource *res) 31{ 32 if (res) { 33 release_resource(res); 34 kfree(res); 35 } 36} 37EXPORT_SYMBOL(release_and_free_resource); 38 39#ifdef CONFIG_SND_VERBOSE_PRINTK 40/* strip the leading path if the given path is absolute */ 41static 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) 51void __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} 93EXPORT_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 */ 110const struct snd_pci_quirk * 111snd_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} 125EXPORT_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 */ 138const struct snd_pci_quirk * 139snd_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} 147EXPORT_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 */ 160struct snd_fasync { 161 struct fasync_struct *fasync; 162 int signal; 163 int poll; 164 int on; 165 struct list_head list; 166}; 167 168static DEFINE_SPINLOCK(snd_fasync_lock); 169static LIST_HEAD(snd_fasync_list); 170 171static 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 187static DECLARE_WORK(snd_fasync_work, snd_fasync_work_fn); 188 189int 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} 216EXPORT_SYMBOL_GPL(snd_fasync_helper); 217 218void 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} 231EXPORT_SYMBOL_GPL(snd_kill_fasync); 232 233void 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} 241EXPORT_SYMBOL_GPL(snd_fasync_free); 242