aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>2014-04-25 09:45:12 -0400
committerTakashi Iwai <tiwai@suse.de>2014-05-26 08:28:41 -0400
commit594ddced821dee39a548efe46d7f834bae013505 (patch)
treec0abaae51d84ef71127c3db3797130eb61701001
parentaa02bb6e60783938d61eefe38346781a646800a6 (diff)
ALSA: fireworks: Add hwdep interface
This interface is designed for mixer/control application. To use hwdep interface, the 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>
-rw-r--r--include/uapi/sound/asound.h3
-rw-r--r--include/uapi/sound/firewire.h3
-rw-r--r--sound/firewire/Kconfig1
-rw-r--r--sound/firewire/fireworks/Makefile2
-rw-r--r--sound/firewire/fireworks/fireworks.c5
-rw-r--r--sound/firewire/fireworks/fireworks.h12
-rw-r--r--sound/firewire/fireworks/fireworks_hwdep.c199
-rw-r--r--sound/firewire/fireworks/fireworks_midi.c25
-rw-r--r--sound/firewire/fireworks/fireworks_pcm.c15
-rw-r--r--sound/firewire/fireworks/fireworks_stream.c39
10 files changed, 296 insertions, 8 deletions
diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index 9fc6219d3848..5dfbfdfd2d12 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -94,9 +94,10 @@ enum {
94 SNDRV_HWDEP_IFACE_HDA, /* HD-audio */ 94 SNDRV_HWDEP_IFACE_HDA, /* HD-audio */
95 SNDRV_HWDEP_IFACE_USB_STREAM, /* direct access to usb stream */ 95 SNDRV_HWDEP_IFACE_USB_STREAM, /* direct access to usb stream */
96 SNDRV_HWDEP_IFACE_FW_DICE, /* TC DICE FireWire device */ 96 SNDRV_HWDEP_IFACE_FW_DICE, /* TC DICE FireWire device */
97 SNDRV_HWDEP_IFACE_FW_FIREWORKS, /* Echo Audio Fireworks based device */
97 98
98 /* Don't forget to change the following: */ 99 /* Don't forget to change the following: */
99 SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_DICE 100 SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_FIREWORKS
100}; 101};
101 102
102struct snd_hwdep_info { 103struct snd_hwdep_info {
diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h
index 59f5961302bf..fc9afb261989 100644
--- a/include/uapi/sound/firewire.h
+++ b/include/uapi/sound/firewire.h
@@ -34,7 +34,8 @@ union snd_firewire_event {
34#define SNDRV_FIREWIRE_IOCTL_UNLOCK _IO('H', 0xfa) 34#define SNDRV_FIREWIRE_IOCTL_UNLOCK _IO('H', 0xfa)
35 35
36#define SNDRV_FIREWIRE_TYPE_DICE 1 36#define SNDRV_FIREWIRE_TYPE_DICE 1
37/* Fireworks, AV/C, RME, MOTU, ... */ 37#define SNDRV_FIREWIRE_TYPE_FIREWORKS 2
38/* AV/C, RME, MOTU, ... */
38 39
39struct snd_firewire_get_info { 40struct snd_firewire_get_info {
40 unsigned int type; /* SNDRV_FIREWIRE_TYPE_xxx */ 41 unsigned int type; /* SNDRV_FIREWIRE_TYPE_xxx */
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index fb39f9cfdfbf..875af0217916 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -66,6 +66,7 @@ config SND_FIREWORKS
66 select SND_FIREWIRE_LIB 66 select SND_FIREWIRE_LIB
67 select SND_RAWMIDI 67 select SND_RAWMIDI
68 select SND_PCM 68 select SND_PCM
69 select SND_HWDEP
69 help 70 help
70 Say Y here to include support for FireWire devices based 71 Say Y here to include support for FireWire devices based
71 on Echo Digital Audio Fireworks board: 72 on Echo Digital Audio Fireworks board:
diff --git a/sound/firewire/fireworks/Makefile b/sound/firewire/fireworks/Makefile
index d7ebf834fece..0c7440826db8 100644
--- a/sound/firewire/fireworks/Makefile
+++ b/sound/firewire/fireworks/Makefile
@@ -1,4 +1,4 @@
1snd-fireworks-objs := fireworks_transaction.o fireworks_command.o \ 1snd-fireworks-objs := fireworks_transaction.o fireworks_command.o \
2 fireworks_stream.o fireworks_proc.o fireworks_midi.o \ 2 fireworks_stream.o fireworks_proc.o fireworks_midi.o \
3 fireworks_pcm.o fireworks.o 3 fireworks_pcm.o fireworks_hwdep.o fireworks.o
4obj-m += snd-fireworks.o 4obj-m += snd-fireworks.o
diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c
index d7877c79e32f..f8d06f56618f 100644
--- a/sound/firewire/fireworks/fireworks.c
+++ b/sound/firewire/fireworks/fireworks.c
@@ -217,6 +217,7 @@ efw_probe(struct fw_unit *unit,
217 efw->unit = unit; 217 efw->unit = unit;
218 mutex_init(&efw->mutex); 218 mutex_init(&efw->mutex);
219 spin_lock_init(&efw->lock); 219 spin_lock_init(&efw->lock);
220 init_waitqueue_head(&efw->hwdep_wait);
220 221
221 err = get_hardware_info(efw); 222 err = get_hardware_info(efw);
222 if (err < 0) 223 if (err < 0)
@@ -236,6 +237,10 @@ efw_probe(struct fw_unit *unit,
236 if (err < 0) 237 if (err < 0)
237 goto error; 238 goto error;
238 239
240 err = snd_efw_create_hwdep_device(efw);
241 if (err < 0)
242 goto error;
243
239 err = snd_efw_stream_init_duplex(efw); 244 err = snd_efw_stream_init_duplex(efw);
240 if (err < 0) 245 if (err < 0)
241 goto error; 246 goto error;
diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
index 36419ca12d6f..4aaf2dce5ec8 100644
--- a/sound/firewire/fireworks/fireworks.h
+++ b/sound/firewire/fireworks/fireworks.h
@@ -24,6 +24,8 @@
24#include <sound/info.h> 24#include <sound/info.h>
25#include <sound/rawmidi.h> 25#include <sound/rawmidi.h>
26#include <sound/pcm_params.h> 26#include <sound/pcm_params.h>
27#include <sound/firewire.h>
28#include <sound/hwdep.h>
27 29
28#include "../packets-buffer.h" 30#include "../packets-buffer.h"
29#include "../iso-resources.h" 31#include "../iso-resources.h"
@@ -90,6 +92,11 @@ struct snd_efw {
90 unsigned int phys_in_grp_count; 92 unsigned int phys_in_grp_count;
91 struct snd_efw_phys_grp phys_out_grps[HWINFO_MAX_CAPS_GROUPS]; 93 struct snd_efw_phys_grp phys_out_grps[HWINFO_MAX_CAPS_GROUPS];
92 struct snd_efw_phys_grp phys_in_grps[HWINFO_MAX_CAPS_GROUPS]; 94 struct snd_efw_phys_grp phys_in_grps[HWINFO_MAX_CAPS_GROUPS];
95
96 /* for uapi */
97 int dev_lock_count;
98 bool dev_lock_changed;
99 wait_queue_head_t hwdep_wait;
93}; 100};
94 101
95struct snd_efw_transaction { 102struct snd_efw_transaction {
@@ -197,6 +204,9 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, int sampling_rate);
197void snd_efw_stream_stop_duplex(struct snd_efw *efw); 204void snd_efw_stream_stop_duplex(struct snd_efw *efw);
198void snd_efw_stream_update_duplex(struct snd_efw *efw); 205void snd_efw_stream_update_duplex(struct snd_efw *efw);
199void snd_efw_stream_destroy_duplex(struct snd_efw *efw); 206void snd_efw_stream_destroy_duplex(struct snd_efw *efw);
207void snd_efw_stream_lock_changed(struct snd_efw *efw);
208int snd_efw_stream_lock_try(struct snd_efw *efw);
209void snd_efw_stream_lock_release(struct snd_efw *efw);
200 210
201void snd_efw_proc_init(struct snd_efw *efw); 211void snd_efw_proc_init(struct snd_efw *efw);
202 212
@@ -205,6 +215,8 @@ int snd_efw_create_midi_devices(struct snd_efw *efw);
205int snd_efw_create_pcm_devices(struct snd_efw *efw); 215int snd_efw_create_pcm_devices(struct snd_efw *efw);
206int snd_efw_get_multiplier_mode(unsigned int sampling_rate, unsigned int *mode); 216int snd_efw_get_multiplier_mode(unsigned int sampling_rate, unsigned int *mode);
207 217
218int snd_efw_create_hwdep_device(struct snd_efw *efw);
219
208#define SND_EFW_DEV_ENTRY(vendor, model) \ 220#define SND_EFW_DEV_ENTRY(vendor, model) \
209{ \ 221{ \
210 .match_flags = IEEE1394_MATCH_VENDOR_ID | \ 222 .match_flags = IEEE1394_MATCH_VENDOR_ID | \
diff --git a/sound/firewire/fireworks/fireworks_hwdep.c b/sound/firewire/fireworks/fireworks_hwdep.c
new file mode 100644
index 000000000000..1cf491dc39a3
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_hwdep.c
@@ -0,0 +1,199 @@
1/*
2 * fireworks_hwdep.c - a part of driver for Fireworks based devices
3 *
4 * Copyright (c) 2013-2014 Takashi Sakamoto
5 *
6 * Licensed under the terms of the GNU General Public License, version 2.
7 */
8
9/*
10 * This codes have three functionalities.
11 *
12 * 1.get information about firewire node
13 * 2.get notification about starting/stopping stream
14 * 3.lock/unlock streaming
15 */
16
17#include "fireworks.h"
18
19static long
20hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
21 loff_t *offset)
22{
23 struct snd_efw *efw = hwdep->private_data;
24 DEFINE_WAIT(wait);
25 union snd_firewire_event event;
26
27 spin_lock_irq(&efw->lock);
28
29 while (!efw->dev_lock_changed) {
30 prepare_to_wait(&efw->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
31 spin_unlock_irq(&efw->lock);
32 schedule();
33 finish_wait(&efw->hwdep_wait, &wait);
34 if (signal_pending(current))
35 return -ERESTARTSYS;
36 spin_lock_irq(&efw->lock);
37 }
38
39 memset(&event, 0, sizeof(event));
40 if (efw->dev_lock_changed) {
41 event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
42 event.lock_status.status = (efw->dev_lock_count > 0);
43 efw->dev_lock_changed = false;
44
45 count = min_t(long, count, sizeof(event.lock_status));
46 }
47
48 spin_unlock_irq(&efw->lock);
49
50 if (copy_to_user(buf, &event, count))
51 return -EFAULT;
52
53 return count;
54}
55
56static unsigned int
57hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait)
58{
59 struct snd_efw *efw = hwdep->private_data;
60 unsigned int events;
61
62 poll_wait(file, &efw->hwdep_wait, wait);
63
64 spin_lock_irq(&efw->lock);
65 if (efw->dev_lock_changed)
66 events = POLLIN | POLLRDNORM;
67 else
68 events = 0;
69 spin_unlock_irq(&efw->lock);
70
71 return events;
72}
73
74static int
75hwdep_get_info(struct snd_efw *efw, void __user *arg)
76{
77 struct fw_device *dev = fw_parent_device(efw->unit);
78 struct snd_firewire_get_info info;
79
80 memset(&info, 0, sizeof(info));
81 info.type = SNDRV_FIREWIRE_TYPE_FIREWORKS;
82 info.card = dev->card->index;
83 *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
84 *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
85 strlcpy(info.device_name, dev_name(&dev->device),
86 sizeof(info.device_name));
87
88 if (copy_to_user(arg, &info, sizeof(info)))
89 return -EFAULT;
90
91 return 0;
92}
93
94static int
95hwdep_lock(struct snd_efw *efw)
96{
97 int err;
98
99 spin_lock_irq(&efw->lock);
100
101 if (efw->dev_lock_count == 0) {
102 efw->dev_lock_count = -1;
103 err = 0;
104 } else {
105 err = -EBUSY;
106 }
107
108 spin_unlock_irq(&efw->lock);
109
110 return err;
111}
112
113static int
114hwdep_unlock(struct snd_efw *efw)
115{
116 int err;
117
118 spin_lock_irq(&efw->lock);
119
120 if (efw->dev_lock_count == -1) {
121 efw->dev_lock_count = 0;
122 err = 0;
123 } else {
124 err = -EBADFD;
125 }
126
127 spin_unlock_irq(&efw->lock);
128
129 return err;
130}
131
132static int
133hwdep_release(struct snd_hwdep *hwdep, struct file *file)
134{
135 struct snd_efw *efw = hwdep->private_data;
136
137 spin_lock_irq(&efw->lock);
138 if (efw->dev_lock_count == -1)
139 efw->dev_lock_count = 0;
140 spin_unlock_irq(&efw->lock);
141
142 return 0;
143}
144
145static int
146hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
147 unsigned int cmd, unsigned long arg)
148{
149 struct snd_efw *efw = hwdep->private_data;
150
151 switch (cmd) {
152 case SNDRV_FIREWIRE_IOCTL_GET_INFO:
153 return hwdep_get_info(efw, (void __user *)arg);
154 case SNDRV_FIREWIRE_IOCTL_LOCK:
155 return hwdep_lock(efw);
156 case SNDRV_FIREWIRE_IOCTL_UNLOCK:
157 return hwdep_unlock(efw);
158 default:
159 return -ENOIOCTLCMD;
160 }
161}
162
163#ifdef CONFIG_COMPAT
164static int
165hwdep_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
175static const struct snd_hwdep_ops hwdep_ops = {
176 .read = hwdep_read,
177 .release = hwdep_release,
178 .poll = hwdep_poll,
179 .ioctl = hwdep_ioctl,
180 .ioctl_compat = hwdep_compat_ioctl,
181};
182
183int snd_efw_create_hwdep_device(struct snd_efw *efw)
184{
185 struct snd_hwdep *hwdep;
186 int err;
187
188 err = snd_hwdep_new(efw->card, "Fireworks", 0, &hwdep);
189 if (err < 0)
190 goto end;
191 strcpy(hwdep->name, "Fireworks");
192 hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREWORKS;
193 hwdep->ops = hwdep_ops;
194 hwdep->private_data = efw;
195 hwdep->exclusive = true;
196end:
197 return err;
198}
199
diff --git a/sound/firewire/fireworks/fireworks_midi.c b/sound/firewire/fireworks/fireworks_midi.c
index cbf34e99237f..4a600d235ec5 100644
--- a/sound/firewire/fireworks/fireworks_midi.c
+++ b/sound/firewire/fireworks/fireworks_midi.c
@@ -11,17 +11,36 @@
11static int midi_capture_open(struct snd_rawmidi_substream *substream) 11static int midi_capture_open(struct snd_rawmidi_substream *substream)
12{ 12{
13 struct snd_efw *efw = substream->rmidi->private_data; 13 struct snd_efw *efw = substream->rmidi->private_data;
14 int err;
15
16 err = snd_efw_stream_lock_try(efw);
17 if (err < 0)
18 goto end;
14 19
15 atomic_inc(&efw->capture_substreams); 20 atomic_inc(&efw->capture_substreams);
16 return snd_efw_stream_start_duplex(efw, 0); 21 err = snd_efw_stream_start_duplex(efw, 0);
22 if (err < 0)
23 snd_efw_stream_lock_release(efw);
24
25end:
26 return err;
17} 27}
18 28
19static int midi_playback_open(struct snd_rawmidi_substream *substream) 29static int midi_playback_open(struct snd_rawmidi_substream *substream)
20{ 30{
21 struct snd_efw *efw = substream->rmidi->private_data; 31 struct snd_efw *efw = substream->rmidi->private_data;
32 int err;
33
34 err = snd_efw_stream_lock_try(efw);
35 if (err < 0)
36 goto end;
22 37
23 atomic_inc(&efw->playback_substreams); 38 atomic_inc(&efw->playback_substreams);
24 return snd_efw_stream_start_duplex(efw, 0); 39 err = snd_efw_stream_start_duplex(efw, 0);
40 if (err < 0)
41 snd_efw_stream_lock_release(efw);
42end:
43 return err;
25} 44}
26 45
27static int midi_capture_close(struct snd_rawmidi_substream *substream) 46static int midi_capture_close(struct snd_rawmidi_substream *substream)
@@ -31,6 +50,7 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream)
31 atomic_dec(&efw->capture_substreams); 50 atomic_dec(&efw->capture_substreams);
32 snd_efw_stream_stop_duplex(efw); 51 snd_efw_stream_stop_duplex(efw);
33 52
53 snd_efw_stream_lock_release(efw);
34 return 0; 54 return 0;
35} 55}
36 56
@@ -41,6 +61,7 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream)
41 atomic_dec(&efw->playback_substreams); 61 atomic_dec(&efw->playback_substreams);
42 snd_efw_stream_stop_duplex(efw); 62 snd_efw_stream_stop_duplex(efw);
43 63
64 snd_efw_stream_lock_release(efw);
44 return 0; 65 return 0;
45} 66}
46 67
diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c
index 15ec4e02b84f..ed211d062202 100644
--- a/sound/firewire/fireworks/fireworks_pcm.c
+++ b/sound/firewire/fireworks/fireworks_pcm.c
@@ -199,13 +199,17 @@ static int pcm_open(struct snd_pcm_substream *substream)
199 unsigned int clock_source; 199 unsigned int clock_source;
200 int err; 200 int err;
201 201
202 err = pcm_init_hw_params(efw, substream); 202 err = snd_efw_stream_lock_try(efw);
203 if (err < 0) 203 if (err < 0)
204 goto end; 204 goto end;
205 205
206 err = pcm_init_hw_params(efw, substream);
207 if (err < 0)
208 goto err_locked;
209
206 err = snd_efw_command_get_clock_source(efw, &clock_source); 210 err = snd_efw_command_get_clock_source(efw, &clock_source);
207 if (err < 0) 211 if (err < 0)
208 goto end; 212 goto err_locked;
209 213
210 /* 214 /*
211 * When source of clock is not internal or any PCM streams are running, 215 * When source of clock is not internal or any PCM streams are running,
@@ -216,7 +220,7 @@ static int pcm_open(struct snd_pcm_substream *substream)
216 amdtp_stream_pcm_running(&efw->rx_stream)) { 220 amdtp_stream_pcm_running(&efw->rx_stream)) {
217 err = snd_efw_command_get_sampling_rate(efw, &sampling_rate); 221 err = snd_efw_command_get_sampling_rate(efw, &sampling_rate);
218 if (err < 0) 222 if (err < 0)
219 goto end; 223 goto err_locked;
220 substream->runtime->hw.rate_min = sampling_rate; 224 substream->runtime->hw.rate_min = sampling_rate;
221 substream->runtime->hw.rate_max = sampling_rate; 225 substream->runtime->hw.rate_max = sampling_rate;
222 } 226 }
@@ -224,10 +228,15 @@ static int pcm_open(struct snd_pcm_substream *substream)
224 snd_pcm_set_sync(substream); 228 snd_pcm_set_sync(substream);
225end: 229end:
226 return err; 230 return err;
231err_locked:
232 snd_efw_stream_lock_release(efw);
233 return err;
227} 234}
228 235
229static int pcm_close(struct snd_pcm_substream *substream) 236static int pcm_close(struct snd_pcm_substream *substream)
230{ 237{
238 struct snd_efw *efw = substream->private_data;
239 snd_efw_stream_lock_release(efw);
231 return 0; 240 return 0;
232} 241}
233 242
diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c
index 1860914a3e0f..eaab8f6bc8b6 100644
--- a/sound/firewire/fireworks/fireworks_stream.c
+++ b/sound/firewire/fireworks/fireworks_stream.c
@@ -331,3 +331,42 @@ void snd_efw_stream_destroy_duplex(struct snd_efw *efw)
331 331
332 mutex_unlock(&efw->mutex); 332 mutex_unlock(&efw->mutex);
333} 333}
334
335void snd_efw_stream_lock_changed(struct snd_efw *efw)
336{
337 efw->dev_lock_changed = true;
338 wake_up(&efw->hwdep_wait);
339}
340
341int snd_efw_stream_lock_try(struct snd_efw *efw)
342{
343 int err;
344
345 spin_lock_irq(&efw->lock);
346
347 /* user land lock this */
348 if (efw->dev_lock_count < 0) {
349 err = -EBUSY;
350 goto end;
351 }
352
353 /* this is the first time */
354 if (efw->dev_lock_count++ == 0)
355 snd_efw_stream_lock_changed(efw);
356 err = 0;
357end:
358 spin_unlock_irq(&efw->lock);
359 return err;
360}
361
362void snd_efw_stream_lock_release(struct snd_efw *efw)
363{
364 spin_lock_irq(&efw->lock);
365
366 if (WARN_ON(efw->dev_lock_count <= 0))
367 goto end;
368 if (--efw->dev_lock_count == 0)
369 snd_efw_stream_lock_changed(efw);
370end:
371 spin_unlock_irq(&efw->lock);
372}