162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Device management routines 462306a36Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/slab.h> 862306a36Sopenharmony_ci#include <linux/time.h> 962306a36Sopenharmony_ci#include <linux/export.h> 1062306a36Sopenharmony_ci#include <linux/errno.h> 1162306a36Sopenharmony_ci#include <sound/core.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/** 1462306a36Sopenharmony_ci * snd_device_new - create an ALSA device component 1562306a36Sopenharmony_ci * @card: the card instance 1662306a36Sopenharmony_ci * @type: the device type, SNDRV_DEV_XXX 1762306a36Sopenharmony_ci * @device_data: the data pointer of this device 1862306a36Sopenharmony_ci * @ops: the operator table 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * Creates a new device component for the given data pointer. 2162306a36Sopenharmony_ci * The device will be assigned to the card and managed together 2262306a36Sopenharmony_ci * by the card. 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * The data pointer plays a role as the identifier, too, so the 2562306a36Sopenharmony_ci * pointer address must be unique and unchanged. 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ciint snd_device_new(struct snd_card *card, enum snd_device_type type, 3062306a36Sopenharmony_ci void *device_data, const struct snd_device_ops *ops) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci struct snd_device *dev; 3362306a36Sopenharmony_ci struct list_head *p; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci if (snd_BUG_ON(!card || !device_data || !ops)) 3662306a36Sopenharmony_ci return -ENXIO; 3762306a36Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 3862306a36Sopenharmony_ci if (!dev) 3962306a36Sopenharmony_ci return -ENOMEM; 4062306a36Sopenharmony_ci INIT_LIST_HEAD(&dev->list); 4162306a36Sopenharmony_ci dev->card = card; 4262306a36Sopenharmony_ci dev->type = type; 4362306a36Sopenharmony_ci dev->state = SNDRV_DEV_BUILD; 4462306a36Sopenharmony_ci dev->device_data = device_data; 4562306a36Sopenharmony_ci dev->ops = ops; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci /* insert the entry in an incrementally sorted list */ 4862306a36Sopenharmony_ci list_for_each_prev(p, &card->devices) { 4962306a36Sopenharmony_ci struct snd_device *pdev = list_entry(p, struct snd_device, list); 5062306a36Sopenharmony_ci if ((unsigned int)pdev->type <= (unsigned int)type) 5162306a36Sopenharmony_ci break; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci list_add(&dev->list, p); 5562306a36Sopenharmony_ci return 0; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ciEXPORT_SYMBOL(snd_device_new); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void __snd_device_disconnect(struct snd_device *dev) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci if (dev->state == SNDRV_DEV_REGISTERED) { 6262306a36Sopenharmony_ci if (dev->ops->dev_disconnect && 6362306a36Sopenharmony_ci dev->ops->dev_disconnect(dev)) 6462306a36Sopenharmony_ci dev_err(dev->card->dev, "device disconnect failure\n"); 6562306a36Sopenharmony_ci dev->state = SNDRV_DEV_DISCONNECTED; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic void __snd_device_free(struct snd_device *dev) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci /* unlink */ 7262306a36Sopenharmony_ci list_del(&dev->list); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci __snd_device_disconnect(dev); 7562306a36Sopenharmony_ci if (dev->ops->dev_free) { 7662306a36Sopenharmony_ci if (dev->ops->dev_free(dev)) 7762306a36Sopenharmony_ci dev_err(dev->card->dev, "device free failure\n"); 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci kfree(dev); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic struct snd_device *look_for_dev(struct snd_card *card, void *device_data) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct snd_device *dev; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci list_for_each_entry(dev, &card->devices, list) 8762306a36Sopenharmony_ci if (dev->device_data == device_data) 8862306a36Sopenharmony_ci return dev; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci return NULL; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/** 9462306a36Sopenharmony_ci * snd_device_disconnect - disconnect the device 9562306a36Sopenharmony_ci * @card: the card instance 9662306a36Sopenharmony_ci * @device_data: the data pointer to disconnect 9762306a36Sopenharmony_ci * 9862306a36Sopenharmony_ci * Turns the device into the disconnection state, invoking 9962306a36Sopenharmony_ci * dev_disconnect callback, if the device was already registered. 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * Usually called from snd_card_disconnect(). 10262306a36Sopenharmony_ci * 10362306a36Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure or if the 10462306a36Sopenharmony_ci * device not found. 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_civoid snd_device_disconnect(struct snd_card *card, void *device_data) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct snd_device *dev; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (snd_BUG_ON(!card || !device_data)) 11162306a36Sopenharmony_ci return; 11262306a36Sopenharmony_ci dev = look_for_dev(card, device_data); 11362306a36Sopenharmony_ci if (dev) 11462306a36Sopenharmony_ci __snd_device_disconnect(dev); 11562306a36Sopenharmony_ci else 11662306a36Sopenharmony_ci dev_dbg(card->dev, "device disconnect %p (from %pS), not found\n", 11762306a36Sopenharmony_ci device_data, __builtin_return_address(0)); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_device_disconnect); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/** 12262306a36Sopenharmony_ci * snd_device_free - release the device from the card 12362306a36Sopenharmony_ci * @card: the card instance 12462306a36Sopenharmony_ci * @device_data: the data pointer to release 12562306a36Sopenharmony_ci * 12662306a36Sopenharmony_ci * Removes the device from the list on the card and invokes the 12762306a36Sopenharmony_ci * callbacks, dev_disconnect and dev_free, corresponding to the state. 12862306a36Sopenharmony_ci * Then release the device. 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_civoid snd_device_free(struct snd_card *card, void *device_data) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct snd_device *dev; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (snd_BUG_ON(!card || !device_data)) 13562306a36Sopenharmony_ci return; 13662306a36Sopenharmony_ci dev = look_for_dev(card, device_data); 13762306a36Sopenharmony_ci if (dev) 13862306a36Sopenharmony_ci __snd_device_free(dev); 13962306a36Sopenharmony_ci else 14062306a36Sopenharmony_ci dev_dbg(card->dev, "device free %p (from %pS), not found\n", 14162306a36Sopenharmony_ci device_data, __builtin_return_address(0)); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ciEXPORT_SYMBOL(snd_device_free); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic int __snd_device_register(struct snd_device *dev) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci if (dev->state == SNDRV_DEV_BUILD) { 14862306a36Sopenharmony_ci if (dev->ops->dev_register) { 14962306a36Sopenharmony_ci int err = dev->ops->dev_register(dev); 15062306a36Sopenharmony_ci if (err < 0) 15162306a36Sopenharmony_ci return err; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci dev->state = SNDRV_DEV_REGISTERED; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci return 0; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci/** 15962306a36Sopenharmony_ci * snd_device_register - register the device 16062306a36Sopenharmony_ci * @card: the card instance 16162306a36Sopenharmony_ci * @device_data: the data pointer to register 16262306a36Sopenharmony_ci * 16362306a36Sopenharmony_ci * Registers the device which was already created via 16462306a36Sopenharmony_ci * snd_device_new(). Usually this is called from snd_card_register(), 16562306a36Sopenharmony_ci * but it can be called later if any new devices are created after 16662306a36Sopenharmony_ci * invocation of snd_card_register(). 16762306a36Sopenharmony_ci * 16862306a36Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure or if the 16962306a36Sopenharmony_ci * device not found. 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_ciint snd_device_register(struct snd_card *card, void *device_data) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct snd_device *dev; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (snd_BUG_ON(!card || !device_data)) 17662306a36Sopenharmony_ci return -ENXIO; 17762306a36Sopenharmony_ci dev = look_for_dev(card, device_data); 17862306a36Sopenharmony_ci if (dev) 17962306a36Sopenharmony_ci return __snd_device_register(dev); 18062306a36Sopenharmony_ci snd_BUG(); 18162306a36Sopenharmony_ci return -ENXIO; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ciEXPORT_SYMBOL(snd_device_register); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/* 18662306a36Sopenharmony_ci * register all the devices on the card. 18762306a36Sopenharmony_ci * called from init.c 18862306a36Sopenharmony_ci */ 18962306a36Sopenharmony_ciint snd_device_register_all(struct snd_card *card) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct snd_device *dev; 19262306a36Sopenharmony_ci int err; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (snd_BUG_ON(!card)) 19562306a36Sopenharmony_ci return -ENXIO; 19662306a36Sopenharmony_ci list_for_each_entry(dev, &card->devices, list) { 19762306a36Sopenharmony_ci err = __snd_device_register(dev); 19862306a36Sopenharmony_ci if (err < 0) 19962306a36Sopenharmony_ci return err; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci return 0; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci/* 20562306a36Sopenharmony_ci * disconnect all the devices on the card. 20662306a36Sopenharmony_ci * called from init.c 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_civoid snd_device_disconnect_all(struct snd_card *card) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct snd_device *dev; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (snd_BUG_ON(!card)) 21362306a36Sopenharmony_ci return; 21462306a36Sopenharmony_ci list_for_each_entry_reverse(dev, &card->devices, list) 21562306a36Sopenharmony_ci __snd_device_disconnect(dev); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci/* 21962306a36Sopenharmony_ci * release all the devices on the card. 22062306a36Sopenharmony_ci * called from init.c 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_civoid snd_device_free_all(struct snd_card *card) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct snd_device *dev, *next; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (snd_BUG_ON(!card)) 22762306a36Sopenharmony_ci return; 22862306a36Sopenharmony_ci list_for_each_entry_safe_reverse(dev, next, &card->devices, list) { 22962306a36Sopenharmony_ci /* exception: free ctl and lowlevel stuff later */ 23062306a36Sopenharmony_ci if (dev->type == SNDRV_DEV_CONTROL || 23162306a36Sopenharmony_ci dev->type == SNDRV_DEV_LOWLEVEL) 23262306a36Sopenharmony_ci continue; 23362306a36Sopenharmony_ci __snd_device_free(dev); 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* free all */ 23762306a36Sopenharmony_ci list_for_each_entry_safe_reverse(dev, next, &card->devices, list) 23862306a36Sopenharmony_ci __snd_device_free(dev); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci/** 24262306a36Sopenharmony_ci * snd_device_get_state - Get the current state of the given device 24362306a36Sopenharmony_ci * @card: the card instance 24462306a36Sopenharmony_ci * @device_data: the data pointer to release 24562306a36Sopenharmony_ci * 24662306a36Sopenharmony_ci * Returns the current state of the given device object. For the valid 24762306a36Sopenharmony_ci * device, either @SNDRV_DEV_BUILD, @SNDRV_DEV_REGISTERED or 24862306a36Sopenharmony_ci * @SNDRV_DEV_DISCONNECTED is returned. 24962306a36Sopenharmony_ci * Or for a non-existing device, -1 is returned as an error. 25062306a36Sopenharmony_ci * 25162306a36Sopenharmony_ci * Return: the current state, or -1 if not found 25262306a36Sopenharmony_ci */ 25362306a36Sopenharmony_ciint snd_device_get_state(struct snd_card *card, void *device_data) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct snd_device *dev; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci dev = look_for_dev(card, device_data); 25862306a36Sopenharmony_ci if (dev) 25962306a36Sopenharmony_ci return dev->state; 26062306a36Sopenharmony_ci return -1; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_device_get_state); 263