diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/firewire/Kconfig | 1 | ||||
-rw-r--r-- | sound/firewire/fireworks/Makefile | 2 | ||||
-rw-r--r-- | sound/firewire/fireworks/fireworks.c | 5 | ||||
-rw-r--r-- | sound/firewire/fireworks/fireworks.h | 12 | ||||
-rw-r--r-- | sound/firewire/fireworks/fireworks_hwdep.c | 199 | ||||
-rw-r--r-- | sound/firewire/fireworks/fireworks_midi.c | 25 | ||||
-rw-r--r-- | sound/firewire/fireworks/fireworks_pcm.c | 15 | ||||
-rw-r--r-- | sound/firewire/fireworks/fireworks_stream.c | 39 |
8 files changed, 292 insertions, 6 deletions
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 @@ | |||
1 | snd-fireworks-objs := fireworks_transaction.o fireworks_command.o \ | 1 | snd-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 |
4 | obj-m += snd-fireworks.o | 4 | obj-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 | ||
95 | struct snd_efw_transaction { | 102 | struct snd_efw_transaction { |
@@ -197,6 +204,9 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, int sampling_rate); | |||
197 | void snd_efw_stream_stop_duplex(struct snd_efw *efw); | 204 | void snd_efw_stream_stop_duplex(struct snd_efw *efw); |
198 | void snd_efw_stream_update_duplex(struct snd_efw *efw); | 205 | void snd_efw_stream_update_duplex(struct snd_efw *efw); |
199 | void snd_efw_stream_destroy_duplex(struct snd_efw *efw); | 206 | void snd_efw_stream_destroy_duplex(struct snd_efw *efw); |
207 | void snd_efw_stream_lock_changed(struct snd_efw *efw); | ||
208 | int snd_efw_stream_lock_try(struct snd_efw *efw); | ||
209 | void snd_efw_stream_lock_release(struct snd_efw *efw); | ||
200 | 210 | ||
201 | void snd_efw_proc_init(struct snd_efw *efw); | 211 | void snd_efw_proc_init(struct snd_efw *efw); |
202 | 212 | ||
@@ -205,6 +215,8 @@ int snd_efw_create_midi_devices(struct snd_efw *efw); | |||
205 | int snd_efw_create_pcm_devices(struct snd_efw *efw); | 215 | int snd_efw_create_pcm_devices(struct snd_efw *efw); |
206 | int snd_efw_get_multiplier_mode(unsigned int sampling_rate, unsigned int *mode); | 216 | int snd_efw_get_multiplier_mode(unsigned int sampling_rate, unsigned int *mode); |
207 | 217 | ||
218 | int 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 | |||
19 | static long | ||
20 | hwdep_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 | |||
56 | static unsigned int | ||
57 | hwdep_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 | |||
74 | static int | ||
75 | hwdep_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 | |||
94 | static int | ||
95 | hwdep_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 | |||
113 | static int | ||
114 | hwdep_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 | |||
132 | static int | ||
133 | hwdep_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 | |||
145 | static int | ||
146 | hwdep_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 | ||
164 | static int | ||
165 | 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 | static 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 | |||
183 | int 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; | ||
196 | end: | ||
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 @@ | |||
11 | static int midi_capture_open(struct snd_rawmidi_substream *substream) | 11 | static 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 | |||
25 | end: | ||
26 | return err; | ||
17 | } | 27 | } |
18 | 28 | ||
19 | static int midi_playback_open(struct snd_rawmidi_substream *substream) | 29 | static 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); | ||
42 | end: | ||
43 | return err; | ||
25 | } | 44 | } |
26 | 45 | ||
27 | static int midi_capture_close(struct snd_rawmidi_substream *substream) | 46 | static 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); |
225 | end: | 229 | end: |
226 | return err; | 230 | return err; |
231 | err_locked: | ||
232 | snd_efw_stream_lock_release(efw); | ||
233 | return err; | ||
227 | } | 234 | } |
228 | 235 | ||
229 | static int pcm_close(struct snd_pcm_substream *substream) | 236 | static 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 | |||
335 | void snd_efw_stream_lock_changed(struct snd_efw *efw) | ||
336 | { | ||
337 | efw->dev_lock_changed = true; | ||
338 | wake_up(&efw->hwdep_wait); | ||
339 | } | ||
340 | |||
341 | int 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; | ||
357 | end: | ||
358 | spin_unlock_irq(&efw->lock); | ||
359 | return err; | ||
360 | } | ||
361 | |||
362 | void 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); | ||
370 | end: | ||
371 | spin_unlock_irq(&efw->lock); | ||
372 | } | ||