1// SPDX-License-Identifier: GPL-2.0-only
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>
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
19static 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
27static 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
36	while (!motu->dev_lock_changed && motu->msg == 0 && !has_dsp_event(motu)) {
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;
51		spin_unlock_irq(&motu->lock);
52
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) {
57		event.motu_notification.type = SNDRV_FIREWIRE_EVENT_MOTU_NOTIFICATION;
58		event.motu_notification.message = motu->msg;
59		motu->msg = 0;
60		spin_unlock_irq(&motu->lock);
61
62		count = min_t(long, count, sizeof(event));
63		if (copy_to_user(buf, &event, count))
64			return -EFAULT;
65	} else if (has_dsp_event(motu)) {
66		size_t consumed = 0;
67		u32 __user *ptr;
68		u32 ev;
69
70		spin_unlock_irq(&motu->lock);
71
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;
90	} else {
91		spin_unlock_irq(&motu->lock);
92
93		count = 0;
94	}
95
96	return count;
97}
98
99static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
100			       poll_table *wait)
101{
102	struct snd_motu *motu = hwdep->private_data;
103	__poll_t events;
104
105	poll_wait(file, &motu->hwdep_wait, wait);
106
107	spin_lock_irq(&motu->lock);
108	if (motu->dev_lock_changed || motu->msg || has_dsp_event(motu))
109		events = EPOLLIN | EPOLLRDNORM;
110	else
111		events = 0;
112	spin_unlock_irq(&motu->lock);
113
114	return events | EPOLLOUT;
115}
116
117static 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]);
127	strscpy(info.device_name, dev_name(&dev->device),
128		sizeof(info.device_name));
129
130	if (copy_to_user(arg, &info, sizeof(info)))
131		return -EFAULT;
132
133	return 0;
134}
135
136static 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
154static 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
172static 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
184static 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);
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	}
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	}
261	default:
262		return -ENOIOCTLCMD;
263	}
264}
265
266#ifdef CONFIG_COMPAT
267static 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
277int 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
299	motu->hwdep = hwdep;
300
301	return 0;
302}
303