xref: /openbmc/linux/sound/firewire/motu/motu-hwdep.c (revision 051d4420)
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 
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 
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 
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 
99 static __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 
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]);
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 
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);
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
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 
299 	motu->hwdep = hwdep;
300 
301 	return 0;
302 }
303