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