Skip to content

Commit f656edd

Browse files
takaswietiwai
authored andcommitted
ALSA: fireface: add hwdep interface
This commit adds hwdep interface so as the other drivers for audio and music units on IEEE 1394 have. This interface is designed for mixer/control applications. By using this interface, an application can get information about firewire node, can lock/unlock kernel streaming and can get notification at starting/stopping kernel streaming. Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp> Signed-off-by: Takashi Iwai <tiwai@suse.de>
1 parent 4b31643 commit f656edd

File tree

9 files changed

+270
-6
lines changed

9 files changed

+270
-6
lines changed

include/uapi/sound/asound.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,10 @@ enum {
108108
SNDRV_HWDEP_IFACE_FW_TASCAM, /* TASCAM FireWire series */
109109
SNDRV_HWDEP_IFACE_LINE6, /* Line6 USB processors */
110110
SNDRV_HWDEP_IFACE_FW_MOTU, /* MOTU FireWire series */
111+
SNDRV_HWDEP_IFACE_FW_FIREFACE, /* RME Fireface series */
111112

112113
/* Don't forget to change the following: */
113-
SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_MOTU
114+
SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_FIREFACE
114115
};
115116

116117
struct snd_hwdep_info {

include/uapi/sound/firewire.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ union snd_firewire_event {
7373
#define SNDRV_FIREWIRE_TYPE_DIGI00X 5
7474
#define SNDRV_FIREWIRE_TYPE_TASCAM 6
7575
#define SNDRV_FIREWIRE_TYPE_MOTU 7
76-
/* RME... */
76+
#define SNDRV_FIREWIRE_TYPE_FIREFACE 8
7777

7878
struct snd_firewire_get_info {
7979
unsigned int type; /* SNDRV_FIREWIRE_TYPE_xxx */

sound/firewire/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ config SND_FIREWIRE_MOTU
155155
config SND_FIREFACE
156156
tristate "RME Fireface series support"
157157
select SND_FIREWIRE_LIB
158+
select SND_HWDEP
158159
help
159160
Say Y here to include support for RME fireface series.
160161

sound/firewire/fireface/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
snd-fireface-objs := ff.o ff-transaction.o ff-midi.o ff-proc.o amdtp-ff.o \
2-
ff-stream.o ff-pcm.o
2+
ff-stream.o ff-pcm.o ff-hwdep.o
33
obj-$(CONFIG_SND_FIREFACE) += snd-fireface.o

sound/firewire/fireface/ff-hwdep.c

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/*
2+
* ff-hwdep.c - a part of driver for RME Fireface series
3+
*
4+
* Copyright (c) 2015-2017 Takashi Sakamoto
5+
*
6+
* Licensed under the terms of the GNU General Public License, version 2.
7+
*/
8+
9+
/*
10+
* This codes give three functionality.
11+
*
12+
* 1.get firewire node information
13+
* 2.get notification about starting/stopping stream
14+
* 3.lock/unlock stream
15+
*/
16+
17+
#include "ff.h"
18+
19+
static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
20+
loff_t *offset)
21+
{
22+
struct snd_ff *ff = hwdep->private_data;
23+
DEFINE_WAIT(wait);
24+
union snd_firewire_event event;
25+
26+
spin_lock_irq(&ff->lock);
27+
28+
while (!ff->dev_lock_changed) {
29+
prepare_to_wait(&ff->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
30+
spin_unlock_irq(&ff->lock);
31+
schedule();
32+
finish_wait(&ff->hwdep_wait, &wait);
33+
if (signal_pending(current))
34+
return -ERESTARTSYS;
35+
spin_lock_irq(&ff->lock);
36+
}
37+
38+
memset(&event, 0, sizeof(event));
39+
if (ff->dev_lock_changed) {
40+
event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
41+
event.lock_status.status = (ff->dev_lock_count > 0);
42+
ff->dev_lock_changed = false;
43+
44+
count = min_t(long, count, sizeof(event.lock_status));
45+
}
46+
47+
spin_unlock_irq(&ff->lock);
48+
49+
if (copy_to_user(buf, &event, count))
50+
return -EFAULT;
51+
52+
return count;
53+
}
54+
55+
static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
56+
poll_table *wait)
57+
{
58+
struct snd_ff *ff = hwdep->private_data;
59+
unsigned int events;
60+
61+
poll_wait(file, &ff->hwdep_wait, wait);
62+
63+
spin_lock_irq(&ff->lock);
64+
if (ff->dev_lock_changed)
65+
events = POLLIN | POLLRDNORM;
66+
else
67+
events = 0;
68+
spin_unlock_irq(&ff->lock);
69+
70+
return events;
71+
}
72+
73+
static int hwdep_get_info(struct snd_ff *ff, void __user *arg)
74+
{
75+
struct fw_device *dev = fw_parent_device(ff->unit);
76+
struct snd_firewire_get_info info;
77+
78+
memset(&info, 0, sizeof(info));
79+
info.type = SNDRV_FIREWIRE_TYPE_FIREFACE;
80+
info.card = dev->card->index;
81+
*(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
82+
*(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
83+
strlcpy(info.device_name, dev_name(&dev->device),
84+
sizeof(info.device_name));
85+
86+
if (copy_to_user(arg, &info, sizeof(info)))
87+
return -EFAULT;
88+
89+
return 0;
90+
}
91+
92+
static int hwdep_lock(struct snd_ff *ff)
93+
{
94+
int err;
95+
96+
spin_lock_irq(&ff->lock);
97+
98+
if (ff->dev_lock_count == 0) {
99+
ff->dev_lock_count = -1;
100+
err = 0;
101+
} else {
102+
err = -EBUSY;
103+
}
104+
105+
spin_unlock_irq(&ff->lock);
106+
107+
return err;
108+
}
109+
110+
static int hwdep_unlock(struct snd_ff *ff)
111+
{
112+
int err;
113+
114+
spin_lock_irq(&ff->lock);
115+
116+
if (ff->dev_lock_count == -1) {
117+
ff->dev_lock_count = 0;
118+
err = 0;
119+
} else {
120+
err = -EBADFD;
121+
}
122+
123+
spin_unlock_irq(&ff->lock);
124+
125+
return err;
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+
spin_lock_irq(&ff->lock);
133+
if (ff->dev_lock_count == -1)
134+
ff->dev_lock_count = 0;
135+
spin_unlock_irq(&ff->lock);
136+
137+
return 0;
138+
}
139+
140+
static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
141+
unsigned int cmd, unsigned long arg)
142+
{
143+
struct snd_ff *ff = hwdep->private_data;
144+
145+
switch (cmd) {
146+
case SNDRV_FIREWIRE_IOCTL_GET_INFO:
147+
return hwdep_get_info(ff, (void __user *)arg);
148+
case SNDRV_FIREWIRE_IOCTL_LOCK:
149+
return hwdep_lock(ff);
150+
case SNDRV_FIREWIRE_IOCTL_UNLOCK:
151+
return hwdep_unlock(ff);
152+
default:
153+
return -ENOIOCTLCMD;
154+
}
155+
}
156+
157+
#ifdef CONFIG_COMPAT
158+
static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
159+
unsigned int cmd, unsigned long arg)
160+
{
161+
return hwdep_ioctl(hwdep, file, cmd,
162+
(unsigned long)compat_ptr(arg));
163+
}
164+
#else
165+
#define hwdep_compat_ioctl NULL
166+
#endif
167+
168+
int snd_ff_create_hwdep_devices(struct snd_ff *ff)
169+
{
170+
static const struct snd_hwdep_ops hwdep_ops = {
171+
.read = hwdep_read,
172+
.release = hwdep_release,
173+
.poll = hwdep_poll,
174+
.ioctl = hwdep_ioctl,
175+
.ioctl_compat = hwdep_compat_ioctl,
176+
};
177+
struct snd_hwdep *hwdep;
178+
int err;
179+
180+
err = snd_hwdep_new(ff->card, ff->card->driver, 0, &hwdep);
181+
if (err < 0)
182+
return err;
183+
184+
strcpy(hwdep->name, ff->card->driver);
185+
hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREFACE;
186+
hwdep->ops = hwdep_ops;
187+
hwdep->private_data = ff;
188+
hwdep->exclusive = true;
189+
190+
return 0;
191+
}

sound/firewire/fireface/ff-pcm.c

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,13 +154,21 @@ static int pcm_open(struct snd_pcm_substream *substream)
154154
enum snd_ff_clock_src src;
155155
int i, err;
156156

157-
err = pcm_init_hw_params(ff, substream);
157+
err = snd_ff_stream_lock_try(ff);
158158
if (err < 0)
159159
return err;
160160

161+
err = pcm_init_hw_params(ff, substream);
162+
if (err < 0) {
163+
snd_ff_stream_lock_release(ff);
164+
return err;
165+
}
166+
161167
err = ff->spec->protocol->get_clock(ff, &rate, &src);
162-
if (err < 0)
168+
if (err < 0) {
169+
snd_ff_stream_lock_release(ff);
163170
return err;
171+
}
164172

165173
if (src != SND_FF_CLOCK_SRC_INTERNAL) {
166174
for (i = 0; i < CIP_SFC_COUNT; ++i) {
@@ -171,8 +179,10 @@ static int pcm_open(struct snd_pcm_substream *substream)
171179
* The unit is configured at sampling frequency which packet
172180
* streaming engine can't support.
173181
*/
174-
if (i >= CIP_SFC_COUNT)
182+
if (i >= CIP_SFC_COUNT) {
183+
snd_ff_stream_lock_release(ff);
175184
return -EIO;
185+
}
176186

177187
substream->runtime->hw.rate_min = rate;
178188
substream->runtime->hw.rate_max = rate;
@@ -192,6 +202,10 @@ static int pcm_open(struct snd_pcm_substream *substream)
192202

193203
static int pcm_close(struct snd_pcm_substream *substream)
194204
{
205+
struct snd_ff *ff = substream->private_data;
206+
207+
snd_ff_stream_lock_release(ff);
208+
195209
return 0;
196210
}
197211

sound/firewire/fireface/ff-stream.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,3 +241,42 @@ void snd_ff_stream_update_duplex(struct snd_ff *ff)
241241
fw_iso_resources_update(&ff->tx_resources);
242242
fw_iso_resources_update(&ff->rx_resources);
243243
}
244+
245+
void snd_ff_stream_lock_changed(struct snd_ff *ff)
246+
{
247+
ff->dev_lock_changed = true;
248+
wake_up(&ff->hwdep_wait);
249+
}
250+
251+
int snd_ff_stream_lock_try(struct snd_ff *ff)
252+
{
253+
int err;
254+
255+
spin_lock_irq(&ff->lock);
256+
257+
/* user land lock this */
258+
if (ff->dev_lock_count < 0) {
259+
err = -EBUSY;
260+
goto end;
261+
}
262+
263+
/* this is the first time */
264+
if (ff->dev_lock_count++ == 0)
265+
snd_ff_stream_lock_changed(ff);
266+
err = 0;
267+
end:
268+
spin_unlock_irq(&ff->lock);
269+
return err;
270+
}
271+
272+
void snd_ff_stream_lock_release(struct snd_ff *ff)
273+
{
274+
spin_lock_irq(&ff->lock);
275+
276+
if (WARN_ON(ff->dev_lock_count <= 0))
277+
goto end;
278+
if (--ff->dev_lock_count == 0)
279+
snd_ff_stream_lock_changed(ff);
280+
end:
281+
spin_unlock_irq(&ff->lock);
282+
}

sound/firewire/fireface/ff.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ static void do_registration(struct work_struct *work)
7676
if (err < 0)
7777
goto error;
7878

79+
err = snd_ff_create_hwdep_devices(ff);
80+
if (err < 0)
81+
goto error;
82+
7983
err = snd_card_register(ff->card);
8084
if (err < 0)
8185
goto error;
@@ -108,6 +112,7 @@ static int snd_ff_probe(struct fw_unit *unit,
108112

109113
mutex_init(&ff->mutex);
110114
spin_lock_init(&ff->lock);
115+
init_waitqueue_head(&ff->hwdep_wait);
111116

112117
ff->spec = (const struct snd_ff_spec *)entry->driver_data;
113118

sound/firewire/fireface/ff.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@
1717
#include <linux/mutex.h>
1818
#include <linux/slab.h>
1919
#include <linux/compat.h>
20+
#include <linux/sched/signal.h>
2021

2122
#include <sound/core.h>
2223
#include <sound/info.h>
2324
#include <sound/rawmidi.h>
2425
#include <sound/pcm.h>
2526
#include <sound/pcm_params.h>
27+
#include <sound/hwdep.h>
28+
#include <sound/firewire.h>
2629

2730
#include "../lib.h"
2831
#include "../amdtp-stream.h"
@@ -77,6 +80,10 @@ struct snd_ff {
7780
struct amdtp_stream rx_stream;
7881
struct fw_iso_resources tx_resources;
7982
struct fw_iso_resources rx_resources;
83+
84+
int dev_lock_count;
85+
bool dev_lock_changed;
86+
wait_queue_head_t hwdep_wait;
8087
};
8188

8289
enum snd_ff_clock_src {
@@ -122,10 +129,16 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate);
122129
void snd_ff_stream_stop_duplex(struct snd_ff *ff);
123130
void snd_ff_stream_update_duplex(struct snd_ff *ff);
124131

132+
void snd_ff_stream_lock_changed(struct snd_ff *ff);
133+
int snd_ff_stream_lock_try(struct snd_ff *ff);
134+
void snd_ff_stream_lock_release(struct snd_ff *ff);
135+
125136
void snd_ff_proc_init(struct snd_ff *ff);
126137

127138
int snd_ff_create_midi_devices(struct snd_ff *ff);
128139

129140
int snd_ff_create_pcm_devices(struct snd_ff *ff);
130141

142+
int snd_ff_create_hwdep_devices(struct snd_ff *ff);
143+
131144
#endif

0 commit comments

Comments
 (0)