Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/firewire/fireface/ff-hwdep.c
29266 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
76
poll_wait(file, &ff->hwdep_wait, wait);
77
78
guard(spinlock_irq)(&ff->lock);
79
if (ff->dev_lock_changed || has_msg(ff))
80
return EPOLLIN | EPOLLRDNORM;
81
else
82
return 0;
83
}
84
85
static int hwdep_get_info(struct snd_ff *ff, void __user *arg)
86
{
87
struct fw_device *dev = fw_parent_device(ff->unit);
88
struct snd_firewire_get_info info;
89
90
memset(&info, 0, sizeof(info));
91
info.type = SNDRV_FIREWIRE_TYPE_FIREFACE;
92
info.card = dev->card->index;
93
*(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
94
*(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
95
strscpy(info.device_name, dev_name(&dev->device),
96
sizeof(info.device_name));
97
98
if (copy_to_user(arg, &info, sizeof(info)))
99
return -EFAULT;
100
101
return 0;
102
}
103
104
static int hwdep_lock(struct snd_ff *ff)
105
{
106
guard(spinlock_irq)(&ff->lock);
107
108
if (ff->dev_lock_count == 0) {
109
ff->dev_lock_count = -1;
110
return 0;
111
} else {
112
return -EBUSY;
113
}
114
}
115
116
static int hwdep_unlock(struct snd_ff *ff)
117
{
118
guard(spinlock_irq)(&ff->lock);
119
120
if (ff->dev_lock_count == -1) {
121
ff->dev_lock_count = 0;
122
return 0;
123
} else {
124
return -EBADFD;
125
}
126
}
127
128
static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
129
{
130
struct snd_ff *ff = hwdep->private_data;
131
132
guard(spinlock_irq)(&ff->lock);
133
if (ff->dev_lock_count == -1)
134
ff->dev_lock_count = 0;
135
136
return 0;
137
}
138
139
static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
140
unsigned int cmd, unsigned long arg)
141
{
142
struct snd_ff *ff = hwdep->private_data;
143
144
switch (cmd) {
145
case SNDRV_FIREWIRE_IOCTL_GET_INFO:
146
return hwdep_get_info(ff, (void __user *)arg);
147
case SNDRV_FIREWIRE_IOCTL_LOCK:
148
return hwdep_lock(ff);
149
case SNDRV_FIREWIRE_IOCTL_UNLOCK:
150
return hwdep_unlock(ff);
151
default:
152
return -ENOIOCTLCMD;
153
}
154
}
155
156
#ifdef CONFIG_COMPAT
157
static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
158
unsigned int cmd, unsigned long arg)
159
{
160
return hwdep_ioctl(hwdep, file, cmd,
161
(unsigned long)compat_ptr(arg));
162
}
163
#else
164
#define hwdep_compat_ioctl NULL
165
#endif
166
167
int snd_ff_create_hwdep_devices(struct snd_ff *ff)
168
{
169
static const struct snd_hwdep_ops hwdep_ops = {
170
.read = hwdep_read,
171
.release = hwdep_release,
172
.poll = hwdep_poll,
173
.ioctl = hwdep_ioctl,
174
.ioctl_compat = hwdep_compat_ioctl,
175
};
176
struct snd_hwdep *hwdep;
177
int err;
178
179
err = snd_hwdep_new(ff->card, ff->card->driver, 0, &hwdep);
180
if (err < 0)
181
return err;
182
183
strscpy(hwdep->name, ff->card->driver);
184
hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREFACE;
185
hwdep->ops = hwdep_ops;
186
hwdep->private_data = ff;
187
hwdep->exclusive = true;
188
189
return 0;
190
}
191
192