Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/firewire/digi00x/digi00x-hwdep.c
26442 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* digi00x-hwdep.c - a part of driver for Digidesign Digi 002/003 family
4
*
5
* Copyright (c) 2014-2015 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
* 4.get asynchronous messaging
15
*/
16
17
#include "digi00x.h"
18
19
static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
20
loff_t *offset)
21
{
22
struct snd_dg00x *dg00x = hwdep->private_data;
23
DEFINE_WAIT(wait);
24
union snd_firewire_event event;
25
26
spin_lock_irq(&dg00x->lock);
27
28
while (!dg00x->dev_lock_changed && dg00x->msg == 0) {
29
prepare_to_wait(&dg00x->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
30
spin_unlock_irq(&dg00x->lock);
31
schedule();
32
finish_wait(&dg00x->hwdep_wait, &wait);
33
if (signal_pending(current))
34
return -ERESTARTSYS;
35
spin_lock_irq(&dg00x->lock);
36
}
37
38
memset(&event, 0, sizeof(event));
39
if (dg00x->dev_lock_changed) {
40
event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
41
event.lock_status.status = (dg00x->dev_lock_count > 0);
42
dg00x->dev_lock_changed = false;
43
44
count = min_t(long, count, sizeof(event.lock_status));
45
} else {
46
event.digi00x_message.type =
47
SNDRV_FIREWIRE_EVENT_DIGI00X_MESSAGE;
48
event.digi00x_message.message = dg00x->msg;
49
dg00x->msg = 0;
50
51
count = min_t(long, count, sizeof(event.digi00x_message));
52
}
53
54
spin_unlock_irq(&dg00x->lock);
55
56
if (copy_to_user(buf, &event, count))
57
return -EFAULT;
58
59
return count;
60
}
61
62
static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
63
poll_table *wait)
64
{
65
struct snd_dg00x *dg00x = hwdep->private_data;
66
__poll_t events;
67
68
poll_wait(file, &dg00x->hwdep_wait, wait);
69
70
spin_lock_irq(&dg00x->lock);
71
if (dg00x->dev_lock_changed || dg00x->msg)
72
events = EPOLLIN | EPOLLRDNORM;
73
else
74
events = 0;
75
spin_unlock_irq(&dg00x->lock);
76
77
return events;
78
}
79
80
static int hwdep_get_info(struct snd_dg00x *dg00x, void __user *arg)
81
{
82
struct fw_device *dev = fw_parent_device(dg00x->unit);
83
struct snd_firewire_get_info info;
84
85
memset(&info, 0, sizeof(info));
86
info.type = SNDRV_FIREWIRE_TYPE_DIGI00X;
87
info.card = dev->card->index;
88
*(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
89
*(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
90
strscpy(info.device_name, dev_name(&dev->device),
91
sizeof(info.device_name));
92
93
if (copy_to_user(arg, &info, sizeof(info)))
94
return -EFAULT;
95
96
return 0;
97
}
98
99
static int hwdep_lock(struct snd_dg00x *dg00x)
100
{
101
int err;
102
103
spin_lock_irq(&dg00x->lock);
104
105
if (dg00x->dev_lock_count == 0) {
106
dg00x->dev_lock_count = -1;
107
err = 0;
108
} else {
109
err = -EBUSY;
110
}
111
112
spin_unlock_irq(&dg00x->lock);
113
114
return err;
115
}
116
117
static int hwdep_unlock(struct snd_dg00x *dg00x)
118
{
119
int err;
120
121
spin_lock_irq(&dg00x->lock);
122
123
if (dg00x->dev_lock_count == -1) {
124
dg00x->dev_lock_count = 0;
125
err = 0;
126
} else {
127
err = -EBADFD;
128
}
129
130
spin_unlock_irq(&dg00x->lock);
131
132
return err;
133
}
134
135
static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
136
{
137
struct snd_dg00x *dg00x = hwdep->private_data;
138
139
spin_lock_irq(&dg00x->lock);
140
if (dg00x->dev_lock_count == -1)
141
dg00x->dev_lock_count = 0;
142
spin_unlock_irq(&dg00x->lock);
143
144
return 0;
145
}
146
147
static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
148
unsigned int cmd, unsigned long arg)
149
{
150
struct snd_dg00x *dg00x = hwdep->private_data;
151
152
switch (cmd) {
153
case SNDRV_FIREWIRE_IOCTL_GET_INFO:
154
return hwdep_get_info(dg00x, (void __user *)arg);
155
case SNDRV_FIREWIRE_IOCTL_LOCK:
156
return hwdep_lock(dg00x);
157
case SNDRV_FIREWIRE_IOCTL_UNLOCK:
158
return hwdep_unlock(dg00x);
159
default:
160
return -ENOIOCTLCMD;
161
}
162
}
163
164
#ifdef CONFIG_COMPAT
165
static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
166
unsigned int cmd, unsigned long arg)
167
{
168
return hwdep_ioctl(hwdep, file, cmd,
169
(unsigned long)compat_ptr(arg));
170
}
171
#else
172
#define hwdep_compat_ioctl NULL
173
#endif
174
175
int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x)
176
{
177
static const struct snd_hwdep_ops ops = {
178
.read = hwdep_read,
179
.release = hwdep_release,
180
.poll = hwdep_poll,
181
.ioctl = hwdep_ioctl,
182
.ioctl_compat = hwdep_compat_ioctl,
183
};
184
struct snd_hwdep *hwdep;
185
int err;
186
187
err = snd_hwdep_new(dg00x->card, "Digi00x", 0, &hwdep);
188
if (err < 0)
189
return err;
190
191
strscpy(hwdep->name, "Digi00x");
192
hwdep->iface = SNDRV_HWDEP_IFACE_FW_DIGI00X;
193
hwdep->ops = ops;
194
hwdep->private_data = dg00x;
195
hwdep->exclusive = true;
196
197
return err;
198
}
199
200