1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Device management routines 4 * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 5 */ 6 7 #include <linux/slab.h> 8 #include <linux/time.h> 9 #include <linux/export.h> 10 #include <linux/errno.h> 11 #include <sound/core.h> 12 13 /** 14 * snd_device_new - create an ALSA device component 15 * @card: the card instance 16 * @type: the device type, SNDRV_DEV_XXX 17 * @device_data: the data pointer of this device 18 * @ops: the operator table 19 * 20 * Creates a new device component for the given data pointer. 21 * The device will be assigned to the card and managed together 22 * by the card. 23 * 24 * The data pointer plays a role as the identifier, too, so the 25 * pointer address must be unique and unchanged. 26 * 27 * Return: Zero if successful, or a negative error code on failure. 28 */ 29 int snd_device_new(struct snd_card *card, enum snd_device_type type, 30 void *device_data, const struct snd_device_ops *ops) 31 { 32 struct snd_device *dev; 33 struct list_head *p; 34 35 if (snd_BUG_ON(!card || !device_data || !ops)) 36 return -ENXIO; 37 dev = kzalloc(sizeof(*dev), GFP_KERNEL); 38 if (!dev) 39 return -ENOMEM; 40 INIT_LIST_HEAD(&dev->list); 41 dev->card = card; 42 dev->type = type; 43 dev->state = SNDRV_DEV_BUILD; 44 dev->device_data = device_data; 45 dev->ops = ops; 46 47 /* insert the entry in an incrementally sorted list */ 48 list_for_each_prev(p, &card->devices) { 49 struct snd_device *pdev = list_entry(p, struct snd_device, list); 50 if ((unsigned int)pdev->type <= (unsigned int)type) 51 break; 52 } 53 54 list_add(&dev->list, p); 55 return 0; 56 } 57 EXPORT_SYMBOL(snd_device_new); 58 59 static void __snd_device_disconnect(struct snd_device *dev) 60 { 61 if (dev->state == SNDRV_DEV_REGISTERED) { 62 if (dev->ops->dev_disconnect && 63 dev->ops->dev_disconnect(dev)) 64 dev_err(dev->card->dev, "device disconnect failure\n"); 65 dev->state = SNDRV_DEV_DISCONNECTED; 66 } 67 } 68 69 static void __snd_device_free(struct snd_device *dev) 70 { 71 /* unlink */ 72 list_del(&dev->list); 73 74 __snd_device_disconnect(dev); 75 if (dev->ops->dev_free) { 76 if (dev->ops->dev_free(dev)) 77 dev_err(dev->card->dev, "device free failure\n"); 78 } 79 kfree(dev); 80 } 81 82 static struct snd_device *look_for_dev(struct snd_card *card, void *device_data) 83 { 84 struct snd_device *dev; 85 86 list_for_each_entry(dev, &card->devices, list) 87 if (dev->device_data == device_data) 88 return dev; 89 90 return NULL; 91 } 92 93 /** 94 * snd_device_disconnect - disconnect the device 95 * @card: the card instance 96 * @device_data: the data pointer to disconnect 97 * 98 * Turns the device into the disconnection state, invoking 99 * dev_disconnect callback, if the device was already registered. 100 * 101 * Usually called from snd_card_disconnect(). 102 * 103 * Return: Zero if successful, or a negative error code on failure or if the 104 * device not found. 105 */ 106 void snd_device_disconnect(struct snd_card *card, void *device_data) 107 { 108 struct snd_device *dev; 109 110 if (snd_BUG_ON(!card || !device_data)) 111 return; 112 dev = look_for_dev(card, device_data); 113 if (dev) 114 __snd_device_disconnect(dev); 115 else 116 dev_dbg(card->dev, "device disconnect %p (from %pS), not found\n", 117 device_data, __builtin_return_address(0)); 118 } 119 EXPORT_SYMBOL_GPL(snd_device_disconnect); 120 121 /** 122 * snd_device_free - release the device from the card 123 * @card: the card instance 124 * @device_data: the data pointer to release 125 * 126 * Removes the device from the list on the card and invokes the 127 * callbacks, dev_disconnect and dev_free, corresponding to the state. 128 * Then release the device. 129 */ 130 void snd_device_free(struct snd_card *card, void *device_data) 131 { 132 struct snd_device *dev; 133 134 if (snd_BUG_ON(!card || !device_data)) 135 return; 136 dev = look_for_dev(card, device_data); 137 if (dev) 138 __snd_device_free(dev); 139 else 140 dev_dbg(card->dev, "device free %p (from %pS), not found\n", 141 device_data, __builtin_return_address(0)); 142 } 143 EXPORT_SYMBOL(snd_device_free); 144 145 static int __snd_device_register(struct snd_device *dev) 146 { 147 if (dev->state == SNDRV_DEV_BUILD) { 148 if (dev->ops->dev_register) { 149 int err = dev->ops->dev_register(dev); 150 if (err < 0) 151 return err; 152 } 153 dev->state = SNDRV_DEV_REGISTERED; 154 } 155 return 0; 156 } 157 158 /** 159 * snd_device_register - register the device 160 * @card: the card instance 161 * @device_data: the data pointer to register 162 * 163 * Registers the device which was already created via 164 * snd_device_new(). Usually this is called from snd_card_register(), 165 * but it can be called later if any new devices are created after 166 * invocation of snd_card_register(). 167 * 168 * Return: Zero if successful, or a negative error code on failure or if the 169 * device not found. 170 */ 171 int snd_device_register(struct snd_card *card, void *device_data) 172 { 173 struct snd_device *dev; 174 175 if (snd_BUG_ON(!card || !device_data)) 176 return -ENXIO; 177 dev = look_for_dev(card, device_data); 178 if (dev) 179 return __snd_device_register(dev); 180 snd_BUG(); 181 return -ENXIO; 182 } 183 EXPORT_SYMBOL(snd_device_register); 184 185 /* 186 * register all the devices on the card. 187 * called from init.c 188 */ 189 int snd_device_register_all(struct snd_card *card) 190 { 191 struct snd_device *dev; 192 int err; 193 194 if (snd_BUG_ON(!card)) 195 return -ENXIO; 196 list_for_each_entry(dev, &card->devices, list) { 197 err = __snd_device_register(dev); 198 if (err < 0) 199 return err; 200 } 201 return 0; 202 } 203 204 /* 205 * disconnect all the devices on the card. 206 * called from init.c 207 */ 208 void snd_device_disconnect_all(struct snd_card *card) 209 { 210 struct snd_device *dev; 211 212 if (snd_BUG_ON(!card)) 213 return; 214 list_for_each_entry_reverse(dev, &card->devices, list) 215 __snd_device_disconnect(dev); 216 } 217 218 /* 219 * release all the devices on the card. 220 * called from init.c 221 */ 222 void snd_device_free_all(struct snd_card *card) 223 { 224 struct snd_device *dev, *next; 225 226 if (snd_BUG_ON(!card)) 227 return; 228 list_for_each_entry_safe_reverse(dev, next, &card->devices, list) { 229 /* exception: free ctl and lowlevel stuff later */ 230 if (dev->type == SNDRV_DEV_CONTROL || 231 dev->type == SNDRV_DEV_LOWLEVEL) 232 continue; 233 __snd_device_free(dev); 234 } 235 236 /* free all */ 237 list_for_each_entry_safe_reverse(dev, next, &card->devices, list) 238 __snd_device_free(dev); 239 } 240