Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/core/device.c
26378 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* Device management routines
4
* Copyright (c) by Jaroslav Kysela <[email protected]>
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
*/
29
int 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
}
57
EXPORT_SYMBOL(snd_device_new);
58
59
static 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
69
static 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
82
static 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
*/
106
void 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
}
119
EXPORT_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
*/
130
void 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
}
143
EXPORT_SYMBOL(snd_device_free);
144
145
static 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
*/
171
int 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
}
183
EXPORT_SYMBOL(snd_device_register);
184
185
/*
186
* register all the devices on the card.
187
* called from init.c
188
*/
189
int 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
*/
208
void 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
*/
222
void 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