18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Device management routines 48c2ecf20Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/slab.h> 88c2ecf20Sopenharmony_ci#include <linux/time.h> 98c2ecf20Sopenharmony_ci#include <linux/export.h> 108c2ecf20Sopenharmony_ci#include <linux/errno.h> 118c2ecf20Sopenharmony_ci#include <sound/core.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci/** 148c2ecf20Sopenharmony_ci * snd_device_new - create an ALSA device component 158c2ecf20Sopenharmony_ci * @card: the card instance 168c2ecf20Sopenharmony_ci * @type: the device type, SNDRV_DEV_XXX 178c2ecf20Sopenharmony_ci * @device_data: the data pointer of this device 188c2ecf20Sopenharmony_ci * @ops: the operator table 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * Creates a new device component for the given data pointer. 218c2ecf20Sopenharmony_ci * The device will be assigned to the card and managed together 228c2ecf20Sopenharmony_ci * by the card. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * The data pointer plays a role as the identifier, too, so the 258c2ecf20Sopenharmony_ci * pointer address must be unique and unchanged. 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure. 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_ciint snd_device_new(struct snd_card *card, enum snd_device_type type, 308c2ecf20Sopenharmony_ci void *device_data, const struct snd_device_ops *ops) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci struct snd_device *dev; 338c2ecf20Sopenharmony_ci struct list_head *p; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci if (snd_BUG_ON(!card || !device_data || !ops)) 368c2ecf20Sopenharmony_ci return -ENXIO; 378c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 388c2ecf20Sopenharmony_ci if (!dev) 398c2ecf20Sopenharmony_ci return -ENOMEM; 408c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dev->list); 418c2ecf20Sopenharmony_ci dev->card = card; 428c2ecf20Sopenharmony_ci dev->type = type; 438c2ecf20Sopenharmony_ci dev->state = SNDRV_DEV_BUILD; 448c2ecf20Sopenharmony_ci dev->device_data = device_data; 458c2ecf20Sopenharmony_ci dev->ops = ops; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci /* insert the entry in an incrementally sorted list */ 488c2ecf20Sopenharmony_ci list_for_each_prev(p, &card->devices) { 498c2ecf20Sopenharmony_ci struct snd_device *pdev = list_entry(p, struct snd_device, list); 508c2ecf20Sopenharmony_ci if ((unsigned int)pdev->type <= (unsigned int)type) 518c2ecf20Sopenharmony_ci break; 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci list_add(&dev->list, p); 558c2ecf20Sopenharmony_ci return 0; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_device_new); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic void __snd_device_disconnect(struct snd_device *dev) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci if (dev->state == SNDRV_DEV_REGISTERED) { 628c2ecf20Sopenharmony_ci if (dev->ops->dev_disconnect && 638c2ecf20Sopenharmony_ci dev->ops->dev_disconnect(dev)) 648c2ecf20Sopenharmony_ci dev_err(dev->card->dev, "device disconnect failure\n"); 658c2ecf20Sopenharmony_ci dev->state = SNDRV_DEV_DISCONNECTED; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic void __snd_device_free(struct snd_device *dev) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci /* unlink */ 728c2ecf20Sopenharmony_ci list_del(&dev->list); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci __snd_device_disconnect(dev); 758c2ecf20Sopenharmony_ci if (dev->ops->dev_free) { 768c2ecf20Sopenharmony_ci if (dev->ops->dev_free(dev)) 778c2ecf20Sopenharmony_ci dev_err(dev->card->dev, "device free failure\n"); 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci kfree(dev); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic struct snd_device *look_for_dev(struct snd_card *card, void *device_data) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct snd_device *dev; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci list_for_each_entry(dev, &card->devices, list) 878c2ecf20Sopenharmony_ci if (dev->device_data == device_data) 888c2ecf20Sopenharmony_ci return dev; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci return NULL; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/** 948c2ecf20Sopenharmony_ci * snd_device_disconnect - disconnect the device 958c2ecf20Sopenharmony_ci * @card: the card instance 968c2ecf20Sopenharmony_ci * @device_data: the data pointer to disconnect 978c2ecf20Sopenharmony_ci * 988c2ecf20Sopenharmony_ci * Turns the device into the disconnection state, invoking 998c2ecf20Sopenharmony_ci * dev_disconnect callback, if the device was already registered. 1008c2ecf20Sopenharmony_ci * 1018c2ecf20Sopenharmony_ci * Usually called from snd_card_disconnect(). 1028c2ecf20Sopenharmony_ci * 1038c2ecf20Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure or if the 1048c2ecf20Sopenharmony_ci * device not found. 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_civoid snd_device_disconnect(struct snd_card *card, void *device_data) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct snd_device *dev; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (snd_BUG_ON(!card || !device_data)) 1118c2ecf20Sopenharmony_ci return; 1128c2ecf20Sopenharmony_ci dev = look_for_dev(card, device_data); 1138c2ecf20Sopenharmony_ci if (dev) 1148c2ecf20Sopenharmony_ci __snd_device_disconnect(dev); 1158c2ecf20Sopenharmony_ci else 1168c2ecf20Sopenharmony_ci dev_dbg(card->dev, "device disconnect %p (from %pS), not found\n", 1178c2ecf20Sopenharmony_ci device_data, __builtin_return_address(0)); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_device_disconnect); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/** 1228c2ecf20Sopenharmony_ci * snd_device_free - release the device from the card 1238c2ecf20Sopenharmony_ci * @card: the card instance 1248c2ecf20Sopenharmony_ci * @device_data: the data pointer to release 1258c2ecf20Sopenharmony_ci * 1268c2ecf20Sopenharmony_ci * Removes the device from the list on the card and invokes the 1278c2ecf20Sopenharmony_ci * callbacks, dev_disconnect and dev_free, corresponding to the state. 1288c2ecf20Sopenharmony_ci * Then release the device. 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_civoid snd_device_free(struct snd_card *card, void *device_data) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct snd_device *dev; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (snd_BUG_ON(!card || !device_data)) 1358c2ecf20Sopenharmony_ci return; 1368c2ecf20Sopenharmony_ci dev = look_for_dev(card, device_data); 1378c2ecf20Sopenharmony_ci if (dev) 1388c2ecf20Sopenharmony_ci __snd_device_free(dev); 1398c2ecf20Sopenharmony_ci else 1408c2ecf20Sopenharmony_ci dev_dbg(card->dev, "device free %p (from %pS), not found\n", 1418c2ecf20Sopenharmony_ci device_data, __builtin_return_address(0)); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_device_free); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int __snd_device_register(struct snd_device *dev) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci if (dev->state == SNDRV_DEV_BUILD) { 1488c2ecf20Sopenharmony_ci if (dev->ops->dev_register) { 1498c2ecf20Sopenharmony_ci int err = dev->ops->dev_register(dev); 1508c2ecf20Sopenharmony_ci if (err < 0) 1518c2ecf20Sopenharmony_ci return err; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci dev->state = SNDRV_DEV_REGISTERED; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/** 1598c2ecf20Sopenharmony_ci * snd_device_register - register the device 1608c2ecf20Sopenharmony_ci * @card: the card instance 1618c2ecf20Sopenharmony_ci * @device_data: the data pointer to register 1628c2ecf20Sopenharmony_ci * 1638c2ecf20Sopenharmony_ci * Registers the device which was already created via 1648c2ecf20Sopenharmony_ci * snd_device_new(). Usually this is called from snd_card_register(), 1658c2ecf20Sopenharmony_ci * but it can be called later if any new devices are created after 1668c2ecf20Sopenharmony_ci * invocation of snd_card_register(). 1678c2ecf20Sopenharmony_ci * 1688c2ecf20Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure or if the 1698c2ecf20Sopenharmony_ci * device not found. 1708c2ecf20Sopenharmony_ci */ 1718c2ecf20Sopenharmony_ciint snd_device_register(struct snd_card *card, void *device_data) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci struct snd_device *dev; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (snd_BUG_ON(!card || !device_data)) 1768c2ecf20Sopenharmony_ci return -ENXIO; 1778c2ecf20Sopenharmony_ci dev = look_for_dev(card, device_data); 1788c2ecf20Sopenharmony_ci if (dev) 1798c2ecf20Sopenharmony_ci return __snd_device_register(dev); 1808c2ecf20Sopenharmony_ci snd_BUG(); 1818c2ecf20Sopenharmony_ci return -ENXIO; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_device_register); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci/* 1868c2ecf20Sopenharmony_ci * register all the devices on the card. 1878c2ecf20Sopenharmony_ci * called from init.c 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_ciint snd_device_register_all(struct snd_card *card) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct snd_device *dev; 1928c2ecf20Sopenharmony_ci int err; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (snd_BUG_ON(!card)) 1958c2ecf20Sopenharmony_ci return -ENXIO; 1968c2ecf20Sopenharmony_ci list_for_each_entry(dev, &card->devices, list) { 1978c2ecf20Sopenharmony_ci err = __snd_device_register(dev); 1988c2ecf20Sopenharmony_ci if (err < 0) 1998c2ecf20Sopenharmony_ci return err; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci return 0; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci/* 2058c2ecf20Sopenharmony_ci * disconnect all the devices on the card. 2068c2ecf20Sopenharmony_ci * called from init.c 2078c2ecf20Sopenharmony_ci */ 2088c2ecf20Sopenharmony_civoid snd_device_disconnect_all(struct snd_card *card) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct snd_device *dev; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (snd_BUG_ON(!card)) 2138c2ecf20Sopenharmony_ci return; 2148c2ecf20Sopenharmony_ci list_for_each_entry_reverse(dev, &card->devices, list) 2158c2ecf20Sopenharmony_ci __snd_device_disconnect(dev); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci/* 2198c2ecf20Sopenharmony_ci * release all the devices on the card. 2208c2ecf20Sopenharmony_ci * called from init.c 2218c2ecf20Sopenharmony_ci */ 2228c2ecf20Sopenharmony_civoid snd_device_free_all(struct snd_card *card) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct snd_device *dev, *next; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (snd_BUG_ON(!card)) 2278c2ecf20Sopenharmony_ci return; 2288c2ecf20Sopenharmony_ci list_for_each_entry_safe_reverse(dev, next, &card->devices, list) { 2298c2ecf20Sopenharmony_ci /* exception: free ctl and lowlevel stuff later */ 2308c2ecf20Sopenharmony_ci if (dev->type == SNDRV_DEV_CONTROL || 2318c2ecf20Sopenharmony_ci dev->type == SNDRV_DEV_LOWLEVEL) 2328c2ecf20Sopenharmony_ci continue; 2338c2ecf20Sopenharmony_ci __snd_device_free(dev); 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci /* free all */ 2378c2ecf20Sopenharmony_ci list_for_each_entry_safe_reverse(dev, next, &card->devices, list) 2388c2ecf20Sopenharmony_ci __snd_device_free(dev); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci/** 2428c2ecf20Sopenharmony_ci * snd_device_get_state - Get the current state of the given device 2438c2ecf20Sopenharmony_ci * @card: the card instance 2448c2ecf20Sopenharmony_ci * @device_data: the data pointer to release 2458c2ecf20Sopenharmony_ci * 2468c2ecf20Sopenharmony_ci * Returns the current state of the given device object. For the valid 2478c2ecf20Sopenharmony_ci * device, either @SNDRV_DEV_BUILD, @SNDRV_DEV_REGISTERED or 2488c2ecf20Sopenharmony_ci * @SNDRV_DEV_DISCONNECTED is returned. 2498c2ecf20Sopenharmony_ci * Or for a non-existing device, -1 is returned as an error. 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_ciint snd_device_get_state(struct snd_card *card, void *device_data) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct snd_device *dev; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci dev = look_for_dev(card, device_data); 2568c2ecf20Sopenharmony_ci if (dev) 2578c2ecf20Sopenharmony_ci return dev->state; 2588c2ecf20Sopenharmony_ci return -1; 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_device_get_state); 261