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