diff options
author | Takashi Sakamoto <o-takashi@sakamocchi.jp> | 2014-04-25 09:45:20 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2014-05-26 08:31:03 -0400 |
commit | 618eabeae711c56d376daa147c6a684116d68705 (patch) | |
tree | 5c3aae69f57c84a57437645d40bcd7c0f1b18e67 /sound/firewire/bebob | |
parent | fbbebd2c40795e87f1280ca4d963f7cbe1c83168 (diff) |
ALSA: bebob: Add hwdep interface
This interface is designed for mixer/control application. By using 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>
Diffstat (limited to 'sound/firewire/bebob')
-rw-r--r-- | sound/firewire/bebob/Makefile | 2 | ||||
-rw-r--r-- | sound/firewire/bebob/bebob.c | 5 | ||||
-rw-r--r-- | sound/firewire/bebob/bebob.h | 13 | ||||
-rw-r--r-- | sound/firewire/bebob/bebob_hwdep.c | 199 | ||||
-rw-r--r-- | sound/firewire/bebob/bebob_midi.c | 24 | ||||
-rw-r--r-- | sound/firewire/bebob/bebob_pcm.c | 16 | ||||
-rw-r--r-- | sound/firewire/bebob/bebob_stream.c | 39 |
7 files changed, 291 insertions, 7 deletions
diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile index 533718a5c1e5..78087772a022 100644 --- a/sound/firewire/bebob/Makefile +++ b/sound/firewire/bebob/Makefile | |||
@@ -1,4 +1,4 @@ | |||
1 | snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \ | 1 | snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \ |
2 | bebob_pcm.o \ | 2 | bebob_pcm.o bebob_hwdep.o \ |
3 | bebob.o | 3 | bebob.o |
4 | obj-m += snd-bebob.o | 4 | obj-m += snd-bebob.o |
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index dbd12c360046..b7d70c2e4e87 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c | |||
@@ -153,6 +153,7 @@ bebob_probe(struct fw_unit *unit, | |||
153 | bebob->unit = unit; | 153 | bebob->unit = unit; |
154 | mutex_init(&bebob->mutex); | 154 | mutex_init(&bebob->mutex); |
155 | spin_lock_init(&bebob->lock); | 155 | spin_lock_init(&bebob->lock); |
156 | init_waitqueue_head(&bebob->hwdep_wait); | ||
156 | 157 | ||
157 | err = name_device(bebob, entry->vendor_id); | 158 | err = name_device(bebob, entry->vendor_id); |
158 | if (err < 0) | 159 | if (err < 0) |
@@ -175,6 +176,10 @@ bebob_probe(struct fw_unit *unit, | |||
175 | if (err < 0) | 176 | if (err < 0) |
176 | goto error; | 177 | goto error; |
177 | 178 | ||
179 | err = snd_bebob_create_hwdep_device(bebob); | ||
180 | if (err < 0) | ||
181 | goto error; | ||
182 | |||
178 | err = snd_bebob_stream_init_duplex(bebob); | 183 | err = snd_bebob_stream_init_duplex(bebob); |
179 | if (err < 0) | 184 | if (err < 0) |
180 | goto error; | 185 | goto error; |
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index b41bb913bac5..e8a5e447ff17 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h | |||
@@ -24,6 +24,8 @@ | |||
24 | #include <sound/rawmidi.h> | 24 | #include <sound/rawmidi.h> |
25 | #include <sound/pcm.h> | 25 | #include <sound/pcm.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 "../lib.h" | 30 | #include "../lib.h" |
29 | #include "../fcp.h" | 31 | #include "../fcp.h" |
@@ -75,6 +77,11 @@ struct snd_bebob { | |||
75 | rx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES]; | 77 | rx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES]; |
76 | 78 | ||
77 | int sync_input_plug; | 79 | int sync_input_plug; |
80 | |||
81 | /* for uapi */ | ||
82 | int dev_lock_count; | ||
83 | bool dev_lock_changed; | ||
84 | wait_queue_head_t hwdep_wait; | ||
78 | }; | 85 | }; |
79 | 86 | ||
80 | static inline int | 87 | static inline int |
@@ -175,12 +182,18 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob); | |||
175 | void snd_bebob_stream_update_duplex(struct snd_bebob *bebob); | 182 | void snd_bebob_stream_update_duplex(struct snd_bebob *bebob); |
176 | void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob); | 183 | void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob); |
177 | 184 | ||
185 | void snd_bebob_stream_lock_changed(struct snd_bebob *bebob); | ||
186 | int snd_bebob_stream_lock_try(struct snd_bebob *bebob); | ||
187 | void snd_bebob_stream_lock_release(struct snd_bebob *bebob); | ||
188 | |||
178 | void snd_bebob_proc_init(struct snd_bebob *bebob); | 189 | void snd_bebob_proc_init(struct snd_bebob *bebob); |
179 | 190 | ||
180 | int snd_bebob_create_midi_devices(struct snd_bebob *bebob); | 191 | int snd_bebob_create_midi_devices(struct snd_bebob *bebob); |
181 | 192 | ||
182 | int snd_bebob_create_pcm_devices(struct snd_bebob *bebob); | 193 | int snd_bebob_create_pcm_devices(struct snd_bebob *bebob); |
183 | 194 | ||
195 | int snd_bebob_create_hwdep_device(struct snd_bebob *bebob); | ||
196 | |||
184 | #define SND_BEBOB_DEV_ENTRY(vendor, model) \ | 197 | #define SND_BEBOB_DEV_ENTRY(vendor, model) \ |
185 | { \ | 198 | { \ |
186 | .match_flags = IEEE1394_MATCH_VENDOR_ID | \ | 199 | .match_flags = IEEE1394_MATCH_VENDOR_ID | \ |
diff --git a/sound/firewire/bebob/bebob_hwdep.c b/sound/firewire/bebob/bebob_hwdep.c new file mode 100644 index 000000000000..ce731f4d8b4f --- /dev/null +++ b/sound/firewire/bebob/bebob_hwdep.c | |||
@@ -0,0 +1,199 @@ | |||
1 | /* | ||
2 | * bebob_hwdep.c - a part of driver for BeBoB 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 give three functionality. | ||
11 | * | ||
12 | * 1.get firewire node infomation | ||
13 | * 2.get notification about starting/stopping stream | ||
14 | * 3.lock/unlock stream | ||
15 | */ | ||
16 | |||
17 | #include "bebob.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_bebob *bebob = hwdep->private_data; | ||
24 | DEFINE_WAIT(wait); | ||
25 | union snd_firewire_event event; | ||
26 | |||
27 | spin_lock_irq(&bebob->lock); | ||
28 | |||
29 | while (!bebob->dev_lock_changed) { | ||
30 | prepare_to_wait(&bebob->hwdep_wait, &wait, TASK_INTERRUPTIBLE); | ||
31 | spin_unlock_irq(&bebob->lock); | ||
32 | schedule(); | ||
33 | finish_wait(&bebob->hwdep_wait, &wait); | ||
34 | if (signal_pending(current)) | ||
35 | return -ERESTARTSYS; | ||
36 | spin_lock_irq(&bebob->lock); | ||
37 | } | ||
38 | |||
39 | memset(&event, 0, sizeof(event)); | ||
40 | if (bebob->dev_lock_changed) { | ||
41 | event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; | ||
42 | event.lock_status.status = (bebob->dev_lock_count > 0); | ||
43 | bebob->dev_lock_changed = false; | ||
44 | |||
45 | count = min_t(long, count, sizeof(event.lock_status)); | ||
46 | } | ||
47 | |||
48 | spin_unlock_irq(&bebob->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_bebob *bebob = hwdep->private_data; | ||
60 | unsigned int events; | ||
61 | |||
62 | poll_wait(file, &bebob->hwdep_wait, wait); | ||
63 | |||
64 | spin_lock_irq(&bebob->lock); | ||
65 | if (bebob->dev_lock_changed) | ||
66 | events = POLLIN | POLLRDNORM; | ||
67 | else | ||
68 | events = 0; | ||
69 | spin_unlock_irq(&bebob->lock); | ||
70 | |||
71 | return events; | ||
72 | } | ||
73 | |||
74 | static int | ||
75 | hwdep_get_info(struct snd_bebob *bebob, void __user *arg) | ||
76 | { | ||
77 | struct fw_device *dev = fw_parent_device(bebob->unit); | ||
78 | struct snd_firewire_get_info info; | ||
79 | |||
80 | memset(&info, 0, sizeof(info)); | ||
81 | info.type = SNDRV_FIREWIRE_TYPE_BEBOB; | ||
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_bebob *bebob) | ||
96 | { | ||
97 | int err; | ||
98 | |||
99 | spin_lock_irq(&bebob->lock); | ||
100 | |||
101 | if (bebob->dev_lock_count == 0) { | ||
102 | bebob->dev_lock_count = -1; | ||
103 | err = 0; | ||
104 | } else { | ||
105 | err = -EBUSY; | ||
106 | } | ||
107 | |||
108 | spin_unlock_irq(&bebob->lock); | ||
109 | |||
110 | return err; | ||
111 | } | ||
112 | |||
113 | static int | ||
114 | hwdep_unlock(struct snd_bebob *bebob) | ||
115 | { | ||
116 | int err; | ||
117 | |||
118 | spin_lock_irq(&bebob->lock); | ||
119 | |||
120 | if (bebob->dev_lock_count == -1) { | ||
121 | bebob->dev_lock_count = 0; | ||
122 | err = 0; | ||
123 | } else { | ||
124 | err = -EBADFD; | ||
125 | } | ||
126 | |||
127 | spin_unlock_irq(&bebob->lock); | ||
128 | |||
129 | return err; | ||
130 | } | ||
131 | |||
132 | static int | ||
133 | hwdep_release(struct snd_hwdep *hwdep, struct file *file) | ||
134 | { | ||
135 | struct snd_bebob *bebob = hwdep->private_data; | ||
136 | |||
137 | spin_lock_irq(&bebob->lock); | ||
138 | if (bebob->dev_lock_count == -1) | ||
139 | bebob->dev_lock_count = 0; | ||
140 | spin_unlock_irq(&bebob->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_bebob *bebob = hwdep->private_data; | ||
150 | |||
151 | switch (cmd) { | ||
152 | case SNDRV_FIREWIRE_IOCTL_GET_INFO: | ||
153 | return hwdep_get_info(bebob, (void __user *)arg); | ||
154 | case SNDRV_FIREWIRE_IOCTL_LOCK: | ||
155 | return hwdep_lock(bebob); | ||
156 | case SNDRV_FIREWIRE_IOCTL_UNLOCK: | ||
157 | return hwdep_unlock(bebob); | ||
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_bebob_create_hwdep_device(struct snd_bebob *bebob) | ||
184 | { | ||
185 | struct snd_hwdep *hwdep; | ||
186 | int err; | ||
187 | |||
188 | err = snd_hwdep_new(bebob->card, "BeBoB", 0, &hwdep); | ||
189 | if (err < 0) | ||
190 | goto end; | ||
191 | strcpy(hwdep->name, "BeBoB"); | ||
192 | hwdep->iface = SNDRV_HWDEP_IFACE_FW_BEBOB; | ||
193 | hwdep->ops = hwdep_ops; | ||
194 | hwdep->private_data = bebob; | ||
195 | hwdep->exclusive = true; | ||
196 | end: | ||
197 | return err; | ||
198 | } | ||
199 | |||
diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c index 120a61b90c59..c04cea2c19a6 100644 --- a/sound/firewire/bebob/bebob_midi.c +++ b/sound/firewire/bebob/bebob_midi.c | |||
@@ -11,17 +11,35 @@ | |||
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_bebob *bebob = substream->rmidi->private_data; | 13 | struct snd_bebob *bebob = substream->rmidi->private_data; |
14 | int err; | ||
15 | |||
16 | err = snd_bebob_stream_lock_try(bebob); | ||
17 | if (err < 0) | ||
18 | goto end; | ||
14 | 19 | ||
15 | atomic_inc(&bebob->capture_substreams); | 20 | atomic_inc(&bebob->capture_substreams); |
16 | return snd_bebob_stream_start_duplex(bebob, 0); | 21 | err = snd_bebob_stream_start_duplex(bebob, 0); |
22 | if (err < 0) | ||
23 | snd_bebob_stream_lock_release(bebob); | ||
24 | end: | ||
25 | return err; | ||
17 | } | 26 | } |
18 | 27 | ||
19 | static int midi_playback_open(struct snd_rawmidi_substream *substream) | 28 | static int midi_playback_open(struct snd_rawmidi_substream *substream) |
20 | { | 29 | { |
21 | struct snd_bebob *bebob = substream->rmidi->private_data; | 30 | struct snd_bebob *bebob = substream->rmidi->private_data; |
31 | int err; | ||
32 | |||
33 | err = snd_bebob_stream_lock_try(bebob); | ||
34 | if (err < 0) | ||
35 | goto end; | ||
22 | 36 | ||
23 | atomic_inc(&bebob->playback_substreams); | 37 | atomic_inc(&bebob->playback_substreams); |
24 | return snd_bebob_stream_start_duplex(bebob, 0); | 38 | err = snd_bebob_stream_start_duplex(bebob, 0); |
39 | if (err < 0) | ||
40 | snd_bebob_stream_lock_release(bebob); | ||
41 | end: | ||
42 | return err; | ||
25 | } | 43 | } |
26 | 44 | ||
27 | static int midi_capture_close(struct snd_rawmidi_substream *substream) | 45 | static int midi_capture_close(struct snd_rawmidi_substream *substream) |
@@ -31,6 +49,7 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream) | |||
31 | atomic_dec(&bebob->capture_substreams); | 49 | atomic_dec(&bebob->capture_substreams); |
32 | snd_bebob_stream_stop_duplex(bebob); | 50 | snd_bebob_stream_stop_duplex(bebob); |
33 | 51 | ||
52 | snd_bebob_stream_lock_release(bebob); | ||
34 | return 0; | 53 | return 0; |
35 | } | 54 | } |
36 | 55 | ||
@@ -41,6 +60,7 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream) | |||
41 | atomic_dec(&bebob->playback_substreams); | 60 | atomic_dec(&bebob->playback_substreams); |
42 | snd_bebob_stream_stop_duplex(bebob); | 61 | snd_bebob_stream_stop_duplex(bebob); |
43 | 62 | ||
63 | snd_bebob_stream_lock_release(bebob); | ||
44 | return 0; | 64 | return 0; |
45 | } | 65 | } |
46 | 66 | ||
diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c index 7474309dad87..9d868171db4e 100644 --- a/sound/firewire/bebob/bebob_pcm.c +++ b/sound/firewire/bebob/bebob_pcm.c | |||
@@ -159,13 +159,17 @@ pcm_open(struct snd_pcm_substream *substream) | |||
159 | bool internal; | 159 | bool internal; |
160 | int err; | 160 | int err; |
161 | 161 | ||
162 | err = pcm_init_hw_params(bebob, substream); | 162 | err = snd_bebob_stream_lock_try(bebob); |
163 | if (err < 0) | 163 | if (err < 0) |
164 | goto end; | 164 | goto end; |
165 | 165 | ||
166 | err = pcm_init_hw_params(bebob, substream); | ||
167 | if (err < 0) | ||
168 | goto err_locked; | ||
169 | |||
166 | err = snd_bebob_stream_check_internal_clock(bebob, &internal); | 170 | err = snd_bebob_stream_check_internal_clock(bebob, &internal); |
167 | if (err < 0) | 171 | if (err < 0) |
168 | goto end; | 172 | goto err_locked; |
169 | 173 | ||
170 | /* | 174 | /* |
171 | * When source of clock is internal or any PCM stream are running, | 175 | * When source of clock is internal or any PCM stream are running, |
@@ -178,7 +182,7 @@ pcm_open(struct snd_pcm_substream *substream) | |||
178 | if (err < 0) { | 182 | if (err < 0) { |
179 | dev_err(&bebob->unit->device, | 183 | dev_err(&bebob->unit->device, |
180 | "fail to get sampling rate: %d\n", err); | 184 | "fail to get sampling rate: %d\n", err); |
181 | goto end; | 185 | goto err_locked; |
182 | } | 186 | } |
183 | 187 | ||
184 | substream->runtime->hw.rate_min = sampling_rate; | 188 | substream->runtime->hw.rate_min = sampling_rate; |
@@ -186,14 +190,18 @@ pcm_open(struct snd_pcm_substream *substream) | |||
186 | } | 190 | } |
187 | 191 | ||
188 | snd_pcm_set_sync(substream); | 192 | snd_pcm_set_sync(substream); |
189 | |||
190 | end: | 193 | end: |
191 | return err; | 194 | return err; |
195 | err_locked: | ||
196 | snd_bebob_stream_lock_release(bebob); | ||
197 | return err; | ||
192 | } | 198 | } |
193 | 199 | ||
194 | static int | 200 | static int |
195 | pcm_close(struct snd_pcm_substream *substream) | 201 | pcm_close(struct snd_pcm_substream *substream) |
196 | { | 202 | { |
203 | struct snd_bebob *bebob = substream->private_data; | ||
204 | snd_bebob_stream_lock_release(bebob); | ||
197 | return 0; | 205 | return 0; |
198 | } | 206 | } |
199 | 207 | ||
diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 46b056c8f2a8..85b4fd661787 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c | |||
@@ -912,3 +912,42 @@ int snd_bebob_stream_discover(struct snd_bebob *bebob) | |||
912 | end: | 912 | end: |
913 | return err; | 913 | return err; |
914 | } | 914 | } |
915 | |||
916 | void snd_bebob_stream_lock_changed(struct snd_bebob *bebob) | ||
917 | { | ||
918 | bebob->dev_lock_changed = true; | ||
919 | wake_up(&bebob->hwdep_wait); | ||
920 | } | ||
921 | |||
922 | int snd_bebob_stream_lock_try(struct snd_bebob *bebob) | ||
923 | { | ||
924 | int err; | ||
925 | |||
926 | spin_lock_irq(&bebob->lock); | ||
927 | |||
928 | /* user land lock this */ | ||
929 | if (bebob->dev_lock_count < 0) { | ||
930 | err = -EBUSY; | ||
931 | goto end; | ||
932 | } | ||
933 | |||
934 | /* this is the first time */ | ||
935 | if (bebob->dev_lock_count++ == 0) | ||
936 | snd_bebob_stream_lock_changed(bebob); | ||
937 | err = 0; | ||
938 | end: | ||
939 | spin_unlock_irq(&bebob->lock); | ||
940 | return err; | ||
941 | } | ||
942 | |||
943 | void snd_bebob_stream_lock_release(struct snd_bebob *bebob) | ||
944 | { | ||
945 | spin_lock_irq(&bebob->lock); | ||
946 | |||
947 | if (WARN_ON(bebob->dev_lock_count <= 0)) | ||
948 | goto end; | ||
949 | if (--bebob->dev_lock_count == 0) | ||
950 | snd_bebob_stream_lock_changed(bebob); | ||
951 | end: | ||
952 | spin_unlock_irq(&bebob->lock); | ||
953 | } | ||