11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * Device management routines
4c1017a4cSJaroslav Kysela * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
51da177e4SLinus Torvalds */
61da177e4SLinus Torvalds
71da177e4SLinus Torvalds #include <linux/slab.h>
81da177e4SLinus Torvalds #include <linux/time.h>
9d81a6d71SPaul Gortmaker #include <linux/export.h>
101da177e4SLinus Torvalds #include <linux/errno.h>
111da177e4SLinus Torvalds #include <sound/core.h>
121da177e4SLinus Torvalds
131da177e4SLinus Torvalds /**
141da177e4SLinus Torvalds * snd_device_new - create an ALSA device component
151da177e4SLinus Torvalds * @card: the card instance
16a3352f01SHenrik Kretzschmar * @type: the device type, SNDRV_DEV_XXX
171da177e4SLinus Torvalds * @device_data: the data pointer of this device
181da177e4SLinus Torvalds * @ops: the operator table
191da177e4SLinus Torvalds *
201da177e4SLinus Torvalds * Creates a new device component for the given data pointer.
211da177e4SLinus Torvalds * The device will be assigned to the card and managed together
221da177e4SLinus Torvalds * by the card.
231da177e4SLinus Torvalds *
241da177e4SLinus Torvalds * The data pointer plays a role as the identifier, too, so the
251da177e4SLinus Torvalds * pointer address must be unique and unchanged.
261da177e4SLinus Torvalds *
27eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure.
281da177e4SLinus Torvalds */
snd_device_new(struct snd_card * card,enum snd_device_type type,void * device_data,const struct snd_device_ops * ops)299ce50543STakashi Iwai int snd_device_new(struct snd_card *card, enum snd_device_type type,
308b575824STakashi Iwai void *device_data, const struct snd_device_ops *ops)
311da177e4SLinus Torvalds {
32512bbd6aSTakashi Iwai struct snd_device *dev;
33289ca025STakashi Iwai struct list_head *p;
341da177e4SLinus Torvalds
357eaa943cSTakashi Iwai if (snd_BUG_ON(!card || !device_data || !ops))
367eaa943cSTakashi Iwai return -ENXIO;
37ca2c0966STakashi Iwai dev = kzalloc(sizeof(*dev), GFP_KERNEL);
38ec0e9937STakashi Iwai if (!dev)
391da177e4SLinus Torvalds return -ENOMEM;
40289ca025STakashi Iwai INIT_LIST_HEAD(&dev->list);
411da177e4SLinus Torvalds dev->card = card;
421da177e4SLinus Torvalds dev->type = type;
431da177e4SLinus Torvalds dev->state = SNDRV_DEV_BUILD;
441da177e4SLinus Torvalds dev->device_data = device_data;
451da177e4SLinus Torvalds dev->ops = ops;
46289ca025STakashi Iwai
47289ca025STakashi Iwai /* insert the entry in an incrementally sorted list */
48289ca025STakashi Iwai list_for_each_prev(p, &card->devices) {
49289ca025STakashi Iwai struct snd_device *pdev = list_entry(p, struct snd_device, list);
50289ca025STakashi Iwai if ((unsigned int)pdev->type <= (unsigned int)type)
51289ca025STakashi Iwai break;
521da177e4SLinus Torvalds }
531da177e4SLinus Torvalds
54289ca025STakashi Iwai list_add(&dev->list, p);
55289ca025STakashi Iwai return 0;
56289ca025STakashi Iwai }
57c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_device_new);
58c0d3fb39STakashi Iwai
__snd_device_disconnect(struct snd_device * dev)59e086e303STakashi Iwai static void __snd_device_disconnect(struct snd_device *dev)
6072620d60STakashi Iwai {
6172620d60STakashi Iwai if (dev->state == SNDRV_DEV_REGISTERED) {
6272620d60STakashi Iwai if (dev->ops->dev_disconnect &&
6372620d60STakashi Iwai dev->ops->dev_disconnect(dev))
6472620d60STakashi Iwai dev_err(dev->card->dev, "device disconnect failure\n");
6572620d60STakashi Iwai dev->state = SNDRV_DEV_DISCONNECTED;
6672620d60STakashi Iwai }
6772620d60STakashi Iwai }
6872620d60STakashi Iwai
__snd_device_free(struct snd_device * dev)6972620d60STakashi Iwai static void __snd_device_free(struct snd_device *dev)
7072620d60STakashi Iwai {
7172620d60STakashi Iwai /* unlink */
7272620d60STakashi Iwai list_del(&dev->list);
7372620d60STakashi Iwai
7472620d60STakashi Iwai __snd_device_disconnect(dev);
7572620d60STakashi Iwai if (dev->ops->dev_free) {
7672620d60STakashi Iwai if (dev->ops->dev_free(dev))
7772620d60STakashi Iwai dev_err(dev->card->dev, "device free failure\n");
7872620d60STakashi Iwai }
7972620d60STakashi Iwai kfree(dev);
8072620d60STakashi Iwai }
8172620d60STakashi Iwai
look_for_dev(struct snd_card * card,void * device_data)82289ca025STakashi Iwai static struct snd_device *look_for_dev(struct snd_card *card, void *device_data)
83289ca025STakashi Iwai {
84289ca025STakashi Iwai struct snd_device *dev;
85289ca025STakashi Iwai
86289ca025STakashi Iwai list_for_each_entry(dev, &card->devices, list)
87289ca025STakashi Iwai if (dev->device_data == device_data)
88289ca025STakashi Iwai return dev;
89289ca025STakashi Iwai
90289ca025STakashi Iwai return NULL;
91289ca025STakashi Iwai }
92289ca025STakashi Iwai
931da177e4SLinus Torvalds /**
94e086e303STakashi Iwai * snd_device_disconnect - disconnect the device
95e086e303STakashi Iwai * @card: the card instance
96e086e303STakashi Iwai * @device_data: the data pointer to disconnect
97e086e303STakashi Iwai *
98e086e303STakashi Iwai * Turns the device into the disconnection state, invoking
99e086e303STakashi Iwai * dev_disconnect callback, if the device was already registered.
100e086e303STakashi Iwai *
101e086e303STakashi Iwai * Usually called from snd_card_disconnect().
102e086e303STakashi Iwai *
103e086e303STakashi Iwai * Return: Zero if successful, or a negative error code on failure or if the
104e086e303STakashi Iwai * device not found.
105e086e303STakashi Iwai */
snd_device_disconnect(struct snd_card * card,void * device_data)106e086e303STakashi Iwai void snd_device_disconnect(struct snd_card *card, void *device_data)
107e086e303STakashi Iwai {
108e086e303STakashi Iwai struct snd_device *dev;
109e086e303STakashi Iwai
110e086e303STakashi Iwai if (snd_BUG_ON(!card || !device_data))
111e086e303STakashi Iwai return;
112e086e303STakashi Iwai dev = look_for_dev(card, device_data);
113e086e303STakashi Iwai if (dev)
114e086e303STakashi Iwai __snd_device_disconnect(dev);
115e086e303STakashi Iwai else
116c8445589SHelge Deller dev_dbg(card->dev, "device disconnect %p (from %pS), not found\n",
117e086e303STakashi Iwai device_data, __builtin_return_address(0));
118e086e303STakashi Iwai }
119e086e303STakashi Iwai EXPORT_SYMBOL_GPL(snd_device_disconnect);
120e086e303STakashi Iwai
121e086e303STakashi Iwai /**
1221da177e4SLinus Torvalds * snd_device_free - release the device from the card
1231da177e4SLinus Torvalds * @card: the card instance
1241da177e4SLinus Torvalds * @device_data: the data pointer to release
1251da177e4SLinus Torvalds *
1261da177e4SLinus Torvalds * Removes the device from the list on the card and invokes the
127c461482cSTakashi Iwai * callbacks, dev_disconnect and dev_free, corresponding to the state.
1281da177e4SLinus Torvalds * Then release the device.
1291da177e4SLinus Torvalds */
snd_device_free(struct snd_card * card,void * device_data)13072620d60STakashi Iwai void snd_device_free(struct snd_card *card, void *device_data)
1311da177e4SLinus Torvalds {
132512bbd6aSTakashi Iwai struct snd_device *dev;
1331da177e4SLinus Torvalds
1347eaa943cSTakashi Iwai if (snd_BUG_ON(!card || !device_data))
13572620d60STakashi Iwai return;
136289ca025STakashi Iwai dev = look_for_dev(card, device_data);
13772620d60STakashi Iwai if (dev)
13872620d60STakashi Iwai __snd_device_free(dev);
13972620d60STakashi Iwai else
140c8445589SHelge Deller dev_dbg(card->dev, "device free %p (from %pS), not found\n",
141f2f9307aSTakashi Iwai device_data, __builtin_return_address(0));
1421da177e4SLinus Torvalds }
143c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_device_free);
144c0d3fb39STakashi Iwai
__snd_device_register(struct snd_device * dev)14572620d60STakashi Iwai static int __snd_device_register(struct snd_device *dev)
1461da177e4SLinus Torvalds {
14772620d60STakashi Iwai if (dev->state == SNDRV_DEV_BUILD) {
14872620d60STakashi Iwai if (dev->ops->dev_register) {
14972620d60STakashi Iwai int err = dev->ops->dev_register(dev);
15072620d60STakashi Iwai if (err < 0)
15172620d60STakashi Iwai return err;
15272620d60STakashi Iwai }
15372620d60STakashi Iwai dev->state = SNDRV_DEV_REGISTERED;
1541da177e4SLinus Torvalds }
1551da177e4SLinus Torvalds return 0;
1561da177e4SLinus Torvalds }
1571da177e4SLinus Torvalds
1581da177e4SLinus Torvalds /**
1591da177e4SLinus Torvalds * snd_device_register - register the device
1601da177e4SLinus Torvalds * @card: the card instance
1611da177e4SLinus Torvalds * @device_data: the data pointer to register
1621da177e4SLinus Torvalds *
1631da177e4SLinus Torvalds * Registers the device which was already created via
1641da177e4SLinus Torvalds * snd_device_new(). Usually this is called from snd_card_register(),
1651da177e4SLinus Torvalds * but it can be called later if any new devices are created after
1661da177e4SLinus Torvalds * invocation of snd_card_register().
1671da177e4SLinus Torvalds *
168eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure or if the
1691da177e4SLinus Torvalds * device not found.
1701da177e4SLinus Torvalds */
snd_device_register(struct snd_card * card,void * device_data)171512bbd6aSTakashi Iwai int snd_device_register(struct snd_card *card, void *device_data)
1721da177e4SLinus Torvalds {
173512bbd6aSTakashi Iwai struct snd_device *dev;
1741da177e4SLinus Torvalds
1757eaa943cSTakashi Iwai if (snd_BUG_ON(!card || !device_data))
1767eaa943cSTakashi Iwai return -ENXIO;
177289ca025STakashi Iwai dev = look_for_dev(card, device_data);
17872620d60STakashi Iwai if (dev)
17972620d60STakashi Iwai return __snd_device_register(dev);
1801da177e4SLinus Torvalds snd_BUG();
1811da177e4SLinus Torvalds return -ENXIO;
1821da177e4SLinus Torvalds }
183c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_device_register);
184c0d3fb39STakashi Iwai
1851da177e4SLinus Torvalds /*
1861da177e4SLinus Torvalds * register all the devices on the card.
1871da177e4SLinus Torvalds * called from init.c
1881da177e4SLinus Torvalds */
snd_device_register_all(struct snd_card * card)189512bbd6aSTakashi Iwai int snd_device_register_all(struct snd_card *card)
1901da177e4SLinus Torvalds {
191512bbd6aSTakashi Iwai struct snd_device *dev;
1921da177e4SLinus Torvalds int err;
1931da177e4SLinus Torvalds
1947eaa943cSTakashi Iwai if (snd_BUG_ON(!card))
1957eaa943cSTakashi Iwai return -ENXIO;
1969244b2c3SJohannes Berg list_for_each_entry(dev, &card->devices, list) {
19772620d60STakashi Iwai err = __snd_device_register(dev);
19872620d60STakashi Iwai if (err < 0)
1991da177e4SLinus Torvalds return err;
2001da177e4SLinus Torvalds }
2011da177e4SLinus Torvalds return 0;
2021da177e4SLinus Torvalds }
2031da177e4SLinus Torvalds
2041da177e4SLinus Torvalds /*
2051da177e4SLinus Torvalds * disconnect all the devices on the card.
2061da177e4SLinus Torvalds * called from init.c
2071da177e4SLinus Torvalds */
snd_device_disconnect_all(struct snd_card * card)208e086e303STakashi Iwai void snd_device_disconnect_all(struct snd_card *card)
2091da177e4SLinus Torvalds {
210512bbd6aSTakashi Iwai struct snd_device *dev;
2111da177e4SLinus Torvalds
2127eaa943cSTakashi Iwai if (snd_BUG_ON(!card))
213e086e303STakashi Iwai return;
214e086e303STakashi Iwai list_for_each_entry_reverse(dev, &card->devices, list)
215e086e303STakashi Iwai __snd_device_disconnect(dev);
2161da177e4SLinus Torvalds }
2171da177e4SLinus Torvalds
2181da177e4SLinus Torvalds /*
2191da177e4SLinus Torvalds * release all the devices on the card.
2201da177e4SLinus Torvalds * called from init.c
2211da177e4SLinus Torvalds */
snd_device_free_all(struct snd_card * card)22272620d60STakashi Iwai void snd_device_free_all(struct snd_card *card)
2231da177e4SLinus Torvalds {
224289ca025STakashi Iwai struct snd_device *dev, *next;
2251da177e4SLinus Torvalds
2267eaa943cSTakashi Iwai if (snd_BUG_ON(!card))
22772620d60STakashi Iwai return;
228dc82e524STakashi Iwai list_for_each_entry_safe_reverse(dev, next, &card->devices, list) {
229dc82e524STakashi Iwai /* exception: free ctl and lowlevel stuff later */
230dc82e524STakashi Iwai if (dev->type == SNDRV_DEV_CONTROL ||
231dc82e524STakashi Iwai dev->type == SNDRV_DEV_LOWLEVEL)
232dc82e524STakashi Iwai continue;
233dc82e524STakashi Iwai __snd_device_free(dev);
234dc82e524STakashi Iwai }
235dc82e524STakashi Iwai
236dc82e524STakashi Iwai /* free all */
23772620d60STakashi Iwai list_for_each_entry_safe_reverse(dev, next, &card->devices, list)
23872620d60STakashi Iwai __snd_device_free(dev);
2391da177e4SLinus Torvalds }
240c208a533STakashi Iwai
241c208a533STakashi Iwai /**
242c208a533STakashi Iwai * snd_device_get_state - Get the current state of the given device
243c208a533STakashi Iwai * @card: the card instance
244c208a533STakashi Iwai * @device_data: the data pointer to release
245c208a533STakashi Iwai *
246c208a533STakashi Iwai * Returns the current state of the given device object. For the valid
247c208a533STakashi Iwai * device, either @SNDRV_DEV_BUILD, @SNDRV_DEV_REGISTERED or
248c208a533STakashi Iwai * @SNDRV_DEV_DISCONNECTED is returned.
249c208a533STakashi Iwai * Or for a non-existing device, -1 is returned as an error.
250*281dee67STakashi Iwai *
251*281dee67STakashi Iwai * Return: the current state, or -1 if not found
252c208a533STakashi Iwai */
snd_device_get_state(struct snd_card * card,void * device_data)253c208a533STakashi Iwai int snd_device_get_state(struct snd_card *card, void *device_data)
254c208a533STakashi Iwai {
255c208a533STakashi Iwai struct snd_device *dev;
256c208a533STakashi Iwai
257c208a533STakashi Iwai dev = look_for_dev(card, device_data);
258c208a533STakashi Iwai if (dev)
259c208a533STakashi Iwai return dev->state;
260c208a533STakashi Iwai return -1;
261c208a533STakashi Iwai }
262c208a533STakashi Iwai EXPORT_SYMBOL_GPL(snd_device_get_state);
263