device.c (6594B)
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 */ 29int 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} 57EXPORT_SYMBOL(snd_device_new); 58 59static 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 69static 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 82static 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 */ 106void 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} 119EXPORT_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 */ 130void 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} 143EXPORT_SYMBOL(snd_device_free); 144 145static 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 */ 171int 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} 183EXPORT_SYMBOL(snd_device_register); 184 185/* 186 * register all the devices on the card. 187 * called from init.c 188 */ 189int 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 */ 208void 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 */ 222void 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 241/** 242 * snd_device_get_state - Get the current state of the given device 243 * @card: the card instance 244 * @device_data: the data pointer to release 245 * 246 * Returns the current state of the given device object. For the valid 247 * device, either @SNDRV_DEV_BUILD, @SNDRV_DEV_REGISTERED or 248 * @SNDRV_DEV_DISCONNECTED is returned. 249 * Or for a non-existing device, -1 is returned as an error. 250 */ 251int snd_device_get_state(struct snd_card *card, void *device_data) 252{ 253 struct snd_device *dev; 254 255 dev = look_for_dev(card, device_data); 256 if (dev) 257 return dev->state; 258 return -1; 259} 260EXPORT_SYMBOL_GPL(snd_device_get_state);