Commit | Line | Data |
---|---|---|
da607e19 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
71c37977 TS |
2 | /* |
3 | * motu-hwdep.c - a part of driver for MOTU FireWire series | |
4 | * | |
5 | * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp> | |
71c37977 TS |
6 | */ |
7 | ||
8 | /* | |
9 | * This codes have five functionalities. | |
10 | * | |
11 | * 1.get information about firewire node | |
12 | * 2.get notification about starting/stopping stream | |
13 | * 3.lock/unlock streaming | |
14 | * | |
15 | */ | |
16 | ||
17 | #include "motu.h" | |
18 | ||
d593f78e TS |
19 | static bool has_dsp_event(struct snd_motu *motu) |
20 | { | |
21 | if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) | |
22 | return (snd_motu_register_dsp_message_parser_count_event(motu) > 0); | |
23 | else | |
24 | return false; | |
25 | } | |
26 | ||
71c37977 TS |
27 | static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, |
28 | loff_t *offset) | |
29 | { | |
30 | struct snd_motu *motu = hwdep->private_data; | |
31 | DEFINE_WAIT(wait); | |
32 | union snd_firewire_event event; | |
33 | ||
34 | spin_lock_irq(&motu->lock); | |
35 | ||
d593f78e | 36 | while (!motu->dev_lock_changed && motu->msg == 0 && !has_dsp_event(motu)) { |
71c37977 TS |
37 | prepare_to_wait(&motu->hwdep_wait, &wait, TASK_INTERRUPTIBLE); |
38 | spin_unlock_irq(&motu->lock); | |
39 | schedule(); | |
40 | finish_wait(&motu->hwdep_wait, &wait); | |
41 | if (signal_pending(current)) | |
42 | return -ERESTARTSYS; | |
43 | spin_lock_irq(&motu->lock); | |
44 | } | |
45 | ||
46 | memset(&event, 0, sizeof(event)); | |
47 | if (motu->dev_lock_changed) { | |
48 | event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; | |
49 | event.lock_status.status = (motu->dev_lock_count > 0); | |
50 | motu->dev_lock_changed = false; | |
634ec0b2 | 51 | spin_unlock_irq(&motu->lock); |
71c37977 | 52 | |
634ec0b2 TS |
53 | count = min_t(long, count, sizeof(event)); |
54 | if (copy_to_user(buf, &event, count)) | |
55 | return -EFAULT; | |
56 | } else if (motu->msg > 0) { | |
5aaab1bf TS |
57 | event.motu_notification.type = SNDRV_FIREWIRE_EVENT_MOTU_NOTIFICATION; |
58 | event.motu_notification.message = motu->msg; | |
59 | motu->msg = 0; | |
634ec0b2 | 60 | spin_unlock_irq(&motu->lock); |
5aaab1bf | 61 | |
634ec0b2 TS |
62 | count = min_t(long, count, sizeof(event)); |
63 | if (copy_to_user(buf, &event, count)) | |
64 | return -EFAULT; | |
d593f78e | 65 | } else if (has_dsp_event(motu)) { |
634ec0b2 TS |
66 | size_t consumed = 0; |
67 | u32 __user *ptr; | |
68 | u32 ev; | |
71c37977 | 69 | |
634ec0b2 | 70 | spin_unlock_irq(&motu->lock); |
71c37977 | 71 | |
634ec0b2 TS |
72 | // Header is filled later. |
73 | consumed += sizeof(event.motu_register_dsp_change); | |
74 | ||
75 | while (consumed < count && | |
76 | snd_motu_register_dsp_message_parser_copy_event(motu, &ev)) { | |
77 | ptr = (u32 __user *)(buf + consumed); | |
78 | if (put_user(ev, ptr)) | |
79 | return -EFAULT; | |
80 | consumed += sizeof(ev); | |
81 | } | |
82 | ||
83 | event.motu_register_dsp_change.type = SNDRV_FIREWIRE_EVENT_MOTU_REGISTER_DSP_CHANGE; | |
84 | event.motu_register_dsp_change.count = | |
85 | (consumed - sizeof(event.motu_register_dsp_change)) / 4; | |
86 | if (copy_to_user(buf, &event, sizeof(event.motu_register_dsp_change))) | |
87 | return -EFAULT; | |
88 | ||
89 | count = consumed; | |
c7a806d9 TS |
90 | } else { |
91 | spin_unlock_irq(&motu->lock); | |
92 | ||
93 | count = 0; | |
634ec0b2 | 94 | } |
71c37977 TS |
95 | |
96 | return count; | |
97 | } | |
98 | ||
680ef72a | 99 | static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file, |
71c37977 TS |
100 | poll_table *wait) |
101 | { | |
102 | struct snd_motu *motu = hwdep->private_data; | |
680ef72a | 103 | __poll_t events; |
71c37977 TS |
104 | |
105 | poll_wait(file, &motu->hwdep_wait, wait); | |
106 | ||
107 | spin_lock_irq(&motu->lock); | |
d593f78e | 108 | if (motu->dev_lock_changed || motu->msg || has_dsp_event(motu)) |
a9a08845 | 109 | events = EPOLLIN | EPOLLRDNORM; |
71c37977 TS |
110 | else |
111 | events = 0; | |
112 | spin_unlock_irq(&motu->lock); | |
113 | ||
a9a08845 | 114 | return events | EPOLLOUT; |
71c37977 TS |
115 | } |
116 | ||
117 | static int hwdep_get_info(struct snd_motu *motu, void __user *arg) | |
118 | { | |
119 | struct fw_device *dev = fw_parent_device(motu->unit); | |
120 | struct snd_firewire_get_info info; | |
121 | ||
122 | memset(&info, 0, sizeof(info)); | |
123 | info.type = SNDRV_FIREWIRE_TYPE_MOTU; | |
124 | info.card = dev->card->index; | |
125 | *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); | |
126 | *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); | |
75b1a8f9 | 127 | strscpy(info.device_name, dev_name(&dev->device), |
71c37977 TS |
128 | sizeof(info.device_name)); |
129 | ||
130 | if (copy_to_user(arg, &info, sizeof(info))) | |
131 | return -EFAULT; | |
132 | ||
133 | return 0; | |
134 | } | |
135 | ||
136 | static int hwdep_lock(struct snd_motu *motu) | |
137 | { | |
138 | int err; | |
139 | ||
140 | spin_lock_irq(&motu->lock); | |
141 | ||
142 | if (motu->dev_lock_count == 0) { | |
143 | motu->dev_lock_count = -1; | |
144 | err = 0; | |
145 | } else { | |
146 | err = -EBUSY; | |
147 | } | |
148 | ||
149 | spin_unlock_irq(&motu->lock); | |
150 | ||
151 | return err; | |
152 | } | |
153 | ||
154 | static int hwdep_unlock(struct snd_motu *motu) | |
155 | { | |
156 | int err; | |
157 | ||
158 | spin_lock_irq(&motu->lock); | |
159 | ||
160 | if (motu->dev_lock_count == -1) { | |
161 | motu->dev_lock_count = 0; | |
162 | err = 0; | |
163 | } else { | |
164 | err = -EBADFD; | |
165 | } | |
166 | ||
167 | spin_unlock_irq(&motu->lock); | |
168 | ||
169 | return err; | |
170 | } | |
171 | ||
172 | static int hwdep_release(struct snd_hwdep *hwdep, struct file *file) | |
173 | { | |
174 | struct snd_motu *motu = hwdep->private_data; | |
175 | ||
176 | spin_lock_irq(&motu->lock); | |
177 | if (motu->dev_lock_count == -1) | |
178 | motu->dev_lock_count = 0; | |
179 | spin_unlock_irq(&motu->lock); | |
180 | ||
181 | return 0; | |
182 | } | |
183 | ||
184 | static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, | |
185 | unsigned int cmd, unsigned long arg) | |
186 | { | |
187 | struct snd_motu *motu = hwdep->private_data; | |
188 | ||
189 | switch (cmd) { | |
190 | case SNDRV_FIREWIRE_IOCTL_GET_INFO: | |
191 | return hwdep_get_info(motu, (void __user *)arg); | |
192 | case SNDRV_FIREWIRE_IOCTL_LOCK: | |
193 | return hwdep_lock(motu); | |
194 | case SNDRV_FIREWIRE_IOCTL_UNLOCK: | |
195 | return hwdep_unlock(motu); | |
58b62ab7 TS |
196 | case SNDRV_FIREWIRE_IOCTL_MOTU_REGISTER_DSP_METER: |
197 | { | |
198 | struct snd_firewire_motu_register_dsp_meter *meter; | |
199 | int err; | |
200 | ||
201 | if (!(motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP)) | |
202 | return -ENXIO; | |
203 | ||
204 | meter = kzalloc(sizeof(*meter), GFP_KERNEL); | |
205 | if (!meter) | |
206 | return -ENOMEM; | |
207 | ||
208 | snd_motu_register_dsp_message_parser_copy_meter(motu, meter); | |
209 | ||
210 | err = copy_to_user((void __user *)arg, meter, sizeof(*meter)); | |
211 | kfree(meter); | |
212 | ||
213 | if (err) | |
214 | return -EFAULT; | |
215 | ||
216 | return 0; | |
217 | } | |
218 | case SNDRV_FIREWIRE_IOCTL_MOTU_COMMAND_DSP_METER: | |
219 | { | |
220 | struct snd_firewire_motu_command_dsp_meter *meter; | |
221 | int err; | |
222 | ||
223 | if (!(motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP)) | |
224 | return -ENXIO; | |
225 | ||
226 | meter = kzalloc(sizeof(*meter), GFP_KERNEL); | |
227 | if (!meter) | |
228 | return -ENOMEM; | |
229 | ||
230 | snd_motu_command_dsp_message_parser_copy_meter(motu, meter); | |
231 | ||
232 | err = copy_to_user((void __user *)arg, meter, sizeof(*meter)); | |
233 | kfree(meter); | |
234 | ||
235 | if (err) | |
236 | return -EFAULT; | |
237 | ||
238 | return 0; | |
239 | } | |
ca15a09c TS |
240 | case SNDRV_FIREWIRE_IOCTL_MOTU_REGISTER_DSP_PARAMETER: |
241 | { | |
242 | struct snd_firewire_motu_register_dsp_parameter *param; | |
243 | int err; | |
244 | ||
245 | if (!(motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP)) | |
246 | return -ENXIO; | |
247 | ||
248 | param = kzalloc(sizeof(*param), GFP_KERNEL); | |
249 | if (!param) | |
250 | return -ENOMEM; | |
251 | ||
252 | snd_motu_register_dsp_message_parser_copy_parameter(motu, param); | |
253 | ||
254 | err = copy_to_user((void __user *)arg, param, sizeof(*param)); | |
255 | kfree(param); | |
256 | if (err) | |
257 | return -EFAULT; | |
258 | ||
259 | return 0; | |
260 | } | |
71c37977 TS |
261 | default: |
262 | return -ENOIOCTLCMD; | |
263 | } | |
264 | } | |
265 | ||
266 | #ifdef CONFIG_COMPAT | |
267 | static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, | |
268 | unsigned int cmd, unsigned long arg) | |
269 | { | |
270 | return hwdep_ioctl(hwdep, file, cmd, | |
271 | (unsigned long)compat_ptr(arg)); | |
272 | } | |
273 | #else | |
274 | #define hwdep_compat_ioctl NULL | |
275 | #endif | |
276 | ||
277 | int snd_motu_create_hwdep_device(struct snd_motu *motu) | |
278 | { | |
279 | static const struct snd_hwdep_ops ops = { | |
280 | .read = hwdep_read, | |
281 | .release = hwdep_release, | |
282 | .poll = hwdep_poll, | |
283 | .ioctl = hwdep_ioctl, | |
284 | .ioctl_compat = hwdep_compat_ioctl, | |
285 | }; | |
286 | struct snd_hwdep *hwdep; | |
287 | int err; | |
288 | ||
289 | err = snd_hwdep_new(motu->card, motu->card->driver, 0, &hwdep); | |
290 | if (err < 0) | |
291 | return err; | |
292 | ||
293 | strcpy(hwdep->name, "MOTU"); | |
294 | hwdep->iface = SNDRV_HWDEP_IFACE_FW_MOTU; | |
295 | hwdep->ops = ops; | |
296 | hwdep->private_data = motu; | |
297 | hwdep->exclusive = true; | |
298 | ||
4c9eda8f TS |
299 | motu->hwdep = hwdep; |
300 | ||
71c37977 TS |
301 | return 0; |
302 | } |