Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/firewire/fireface/ff-hwdep.c
26424 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* ff-hwdep.c - a part of driver for RME Fireface series
4
*
5
* Copyright (c) 2015-2017 Takashi Sakamoto
6
*/
7
8
/*
9
* This codes give three functionality.
10
*
11
* 1.get firewire node information
12
* 2.get notification about starting/stopping stream
13
* 3.lock/unlock stream
14
*/
15
16
#include "ff.h"
17
18
static bool has_msg(struct snd_ff *ff)
19
{
20
if (ff->spec->protocol->has_msg)
21
return ff->spec->protocol->has_msg(ff);
22
else
23
return 0;
24
}
25
26
static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
27
loff_t *offset)
28
{
29
struct snd_ff *ff = hwdep->private_data;
30
DEFINE_WAIT(wait);
31
32
spin_lock_irq(&ff->lock);
33
34
while (!ff->dev_lock_changed && !has_msg(ff)) {
35
prepare_to_wait(&ff->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
36
spin_unlock_irq(&ff->lock);
37
schedule();
38
finish_wait(&ff->hwdep_wait, &wait);
39
if (signal_pending(current))
40
return -ERESTARTSYS;
41
spin_lock_irq(&ff->lock);
42
}
43
44
if (ff->dev_lock_changed && count >= sizeof(struct snd_firewire_event_lock_status)) {
45
struct snd_firewire_event_lock_status ev = {
46
.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,
47
.status = (ff->dev_lock_count > 0),
48
};
49
50
ff->dev_lock_changed = false;
51
52
spin_unlock_irq(&ff->lock);
53
54
if (copy_to_user(buf, &ev, sizeof(ev)))
55
return -EFAULT;
56
count = sizeof(ev);
57
} else if (has_msg(ff)) {
58
// NOTE: Acquired spin lock should be released before accessing to user space in the
59
// callback since the access can cause page fault.
60
count = ff->spec->protocol->copy_msg_to_user(ff, buf, count);
61
spin_unlock_irq(&ff->lock);
62
} else {
63
spin_unlock_irq(&ff->lock);
64
65
count = 0;
66
}
67
68
return count;
69
}
70
71
static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
72
poll_table *wait)
73
{
74
struct snd_ff *ff = hwdep->private_data;
75
__poll_t events;
76
77
poll_wait(file, &ff->hwdep_wait, wait);
78
79
spin_lock_irq(&ff->lock);
80
if (ff->dev_lock_changed || has_msg(ff))
81
events = EPOLLIN | EPOLLRDNORM;
82
else
83
events = 0;
84
spin_unlock_irq(&ff->lock);
85
86
return events;
87
}
88
89
static int hwdep_get_info(struct snd_ff *ff, void __user *arg)
90
{
91
struct fw_device *dev = fw_parent_device(ff->unit);
92
struct snd_firewire_get_info info;
93
94
memset(&info, 0, sizeof(info));
95
info.type = SNDRV_FIREWIRE_TYPE_FIREFACE;
96
info.card = dev->card->index;
97
*(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
98
*(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
99
strscpy(info.device_name, dev_name(&dev->device),
100
sizeof(info.device_name));
101
102
if (copy_to_user(arg, &info, sizeof(info)))
103
return -EFAULT;
104
105
return 0;
106
}
107
108
static int hwdep_lock(struct snd_ff *ff)
109
{
110
int err;
111
112
spin_lock_irq(&ff->lock);
113
114
if (ff->dev_lock_count == 0) {
115
ff->dev_lock_count = -1;
116
err = 0;
117
} else {
118
err = -EBUSY;
119
}
120
121
spin_unlock_irq(&ff->lock);
122
123
return err;
124
}
125
126
static int hwdep_unlock(struct snd_ff *ff)
127
{
128
int err;
129
130
spin_lock_irq(&ff->lock);
131
132
if (ff->dev_lock_count == -1) {
133
ff->dev_lock_count = 0;
134
err = 0;
135
} else {
136
err = -EBADFD;
137
}
138
139
spin_unlock_irq(&ff->lock);
140
141
return err;
142
}
143
144
static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
145
{
146
struct snd_ff *ff = hwdep->private_data;
147
148
spin_lock_irq(&ff->lock);
149
if (ff->dev_lock_count == -1)
150
ff->dev_lock_count = 0;
151
spin_unlock_irq(&ff->lock);
152
153
return 0;
154
}
155
156
static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
157
unsigned int cmd, unsigned long arg)
158
{
159
struct snd_ff *ff = hwdep->private_data;
160
161
switch (cmd) {
162
case SNDRV_FIREWIRE_IOCTL_GET_INFO:
163
return hwdep_get_info(ff, (void __user *)arg);
164
case SNDRV_FIREWIRE_IOCTL_LOCK:
165
return hwdep_lock(ff);
166
case SNDRV_FIREWIRE_IOCTL_UNLOCK:
167
return hwdep_unlock(ff);
168
default:
169
return -ENOIOCTLCMD;
170
}
171
}
172
173
#ifdef CONFIG_COMPAT
174
static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
175
unsigned int cmd, unsigned long arg)
176
{
177
return hwdep_ioctl(hwdep, file, cmd,
178
(unsigned long)compat_ptr(arg));
179
}
180
#else
181
#define hwdep_compat_ioctl NULL
182
#endif
183
184
int snd_ff_create_hwdep_devices(struct snd_ff *ff)
185
{
186
static const struct snd_hwdep_ops hwdep_ops = {
187
.read = hwdep_read,
188
.release = hwdep_release,
189
.poll = hwdep_poll,
190
.ioctl = hwdep_ioctl,
191
.ioctl_compat = hwdep_compat_ioctl,
192
};
193
struct snd_hwdep *hwdep;
194
int err;
195
196
err = snd_hwdep_new(ff->card, ff->card->driver, 0, &hwdep);
197
if (err < 0)
198
return err;
199
200
strscpy(hwdep->name, ff->card->driver);
201
hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREFACE;
202
hwdep->ops = hwdep_ops;
203
hwdep->private_data = ff;
204
hwdep->exclusive = true;
205
206
return 0;
207
}
208
209