File Index
Files and directories
Sign in
FILE

motu-hwdep.c

File preview with syntax highlighting.

linux-6.19.14/sound/firewire/motu/motu-hwdep.c C
  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 (consumed + sizeof(ev) > count || 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,
 87				 min_t(long, count, sizeof(event.motu_register_dsp_change))))
 88			return -EFAULT;
 89
 90		count = min_t(long, count, consumed);
 91	} else {
 92		spin_unlock_irq(&motu->lock);
 93
 94		count = 0;
 95	}
 96
 97	return count;
 98}
 99
100static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
101			       poll_table *wait)
102{
103	struct snd_motu *motu = hwdep->private_data;
104
105	poll_wait(file, &motu->hwdep_wait, wait);
106
107	guard(spinlock_irq)(&motu->lock);
108	if (motu->dev_lock_changed || motu->msg || has_dsp_event(motu))
109		return EPOLLIN | EPOLLRDNORM;
110	else
111		return 0;
112}
113
114static int hwdep_get_info(struct snd_motu *motu, void __user *arg)
115{
116	struct fw_device *dev = fw_parent_device(motu->unit);
117	struct snd_firewire_get_info info;
118
119	memset(&info, 0, sizeof(info));
120	info.type = SNDRV_FIREWIRE_TYPE_MOTU;
121	info.card = dev->card->index;
122	*(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
123	*(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
124	strscpy(info.device_name, dev_name(&dev->device),
125		sizeof(info.device_name));
126
127	if (copy_to_user(arg, &info, sizeof(info)))
128		return -EFAULT;
129
130	return 0;
131}
132
133static int hwdep_lock(struct snd_motu *motu)
134{
135	guard(spinlock_irq)(&motu->lock);
136
137	if (motu->dev_lock_count == 0) {
138		motu->dev_lock_count = -1;
139		return 0;
140	} else {
141		return -EBUSY;
142	}
143}
144
145static int hwdep_unlock(struct snd_motu *motu)
146{
147	guard(spinlock_irq)(&motu->lock);
148
149	if (motu->dev_lock_count == -1) {
150		motu->dev_lock_count = 0;
151		return 0;
152	} else {
153		return -EBADFD;
154	}
155}
156
157static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
158{
159	struct snd_motu *motu = hwdep->private_data;
160
161	guard(spinlock_irq)(&motu->lock);
162	if (motu->dev_lock_count == -1)
163		motu->dev_lock_count = 0;
164
165	return 0;
166}
167
168static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
169	    unsigned int cmd, unsigned long arg)
170{
171	struct snd_motu *motu = hwdep->private_data;
172
173	switch (cmd) {
174	case SNDRV_FIREWIRE_IOCTL_GET_INFO:
175		return hwdep_get_info(motu, (void __user *)arg);
176	case SNDRV_FIREWIRE_IOCTL_LOCK:
177		return hwdep_lock(motu);
178	case SNDRV_FIREWIRE_IOCTL_UNLOCK:
179		return hwdep_unlock(motu);
180	case SNDRV_FIREWIRE_IOCTL_MOTU_REGISTER_DSP_METER:
181	{
182		struct snd_firewire_motu_register_dsp_meter *meter;
183		int err;
184
185		if (!(motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP))
186			return -ENXIO;
187
188		meter = kzalloc(sizeof(*meter), GFP_KERNEL);
189		if (!meter)
190			return -ENOMEM;
191
192		snd_motu_register_dsp_message_parser_copy_meter(motu, meter);
193
194		err = copy_to_user((void __user *)arg, meter, sizeof(*meter));
195		kfree(meter);
196
197		if (err)
198			return -EFAULT;
199
200		return 0;
201	}
202	case SNDRV_FIREWIRE_IOCTL_MOTU_COMMAND_DSP_METER:
203	{
204		struct snd_firewire_motu_command_dsp_meter *meter;
205		int err;
206
207		if (!(motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP))
208			return -ENXIO;
209
210		meter = kzalloc(sizeof(*meter), GFP_KERNEL);
211		if (!meter)
212			return -ENOMEM;
213
214		snd_motu_command_dsp_message_parser_copy_meter(motu, meter);
215
216		err = copy_to_user((void __user *)arg, meter, sizeof(*meter));
217		kfree(meter);
218
219		if (err)
220			return -EFAULT;
221
222		return 0;
223	}
224	case SNDRV_FIREWIRE_IOCTL_MOTU_REGISTER_DSP_PARAMETER:
225	{
226		struct snd_firewire_motu_register_dsp_parameter *param;
227		int err;
228
229		if (!(motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP))
230			return -ENXIO;
231
232		param = kzalloc(sizeof(*param), GFP_KERNEL);
233		if (!param)
234			return -ENOMEM;
235
236		snd_motu_register_dsp_message_parser_copy_parameter(motu, param);
237
238		err = copy_to_user((void __user *)arg, param, sizeof(*param));
239		kfree(param);
240		if (err)
241			return -EFAULT;
242
243		return 0;
244	}
245	default:
246		return -ENOIOCTLCMD;
247	}
248}
249
250#ifdef CONFIG_COMPAT
251static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
252			      unsigned int cmd, unsigned long arg)
253{
254	return hwdep_ioctl(hwdep, file, cmd,
255			   (unsigned long)compat_ptr(arg));
256}
257#else
258#define hwdep_compat_ioctl NULL
259#endif
260
261int snd_motu_create_hwdep_device(struct snd_motu *motu)
262{
263	static const struct snd_hwdep_ops ops = {
264		.read		= hwdep_read,
265		.release	= hwdep_release,
266		.poll		= hwdep_poll,
267		.ioctl		= hwdep_ioctl,
268		.ioctl_compat	= hwdep_compat_ioctl,
269	};
270	struct snd_hwdep *hwdep;
271	int err;
272
273	err = snd_hwdep_new(motu->card, motu->card->driver, 0, &hwdep);
274	if (err < 0)
275		return err;
276
277	strscpy(hwdep->name, "MOTU");
278	hwdep->iface = SNDRV_HWDEP_IFACE_FW_MOTU;
279	hwdep->ops = ops;
280	hwdep->private_data = motu;
281	hwdep->exclusive = true;
282
283	motu->hwdep = hwdep;
284
285	return 0;
286}
~/ Read-only file preview.