brintos

brintos / linux-shallow public Read only

0
0
Text · 4.4 KiB · 8a741b3 Raw
208 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * ff-hwdep.c - a part of driver for RME Fireface series4 *5 * Copyright (c) 2015-2017 Takashi Sakamoto6 */7 8/*9 * This codes give three functionality.10 *11 * 1.get firewire node information12 * 2.get notification about starting/stopping stream13 * 3.lock/unlock stream14 */15 16#include "ff.h"17 18static bool has_msg(struct snd_ff *ff)19{20	if (ff->spec->protocol->has_msg)21		return ff->spec->protocol->has_msg(ff);22	else23		return 0;24}25 26static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf,  long count,27		       loff_t *offset)28{29	struct snd_ff *ff = hwdep->private_data;30	DEFINE_WAIT(wait);31 32	spin_lock_irq(&ff->lock);33 34	while (!ff->dev_lock_changed && !has_msg(ff)) {35		prepare_to_wait(&ff->hwdep_wait, &wait, TASK_INTERRUPTIBLE);36		spin_unlock_irq(&ff->lock);37		schedule();38		finish_wait(&ff->hwdep_wait, &wait);39		if (signal_pending(current))40			return -ERESTARTSYS;41		spin_lock_irq(&ff->lock);42	}43 44	if (ff->dev_lock_changed && count >= sizeof(struct snd_firewire_event_lock_status)) {45		struct snd_firewire_event_lock_status ev = {46			.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,47			.status = (ff->dev_lock_count > 0),48		};49 50		ff->dev_lock_changed = false;51 52		spin_unlock_irq(&ff->lock);53 54		if (copy_to_user(buf, &ev, sizeof(ev)))55			return -EFAULT;56		count = sizeof(ev);57	} else if (has_msg(ff)) {58		// NOTE: Acquired spin lock should be released before accessing to user space in the59		// callback since the access can cause page fault.60		count = ff->spec->protocol->copy_msg_to_user(ff, buf, count);61		spin_unlock_irq(&ff->lock);62	} else {63		spin_unlock_irq(&ff->lock);64 65		count = 0;66	}67 68	return count;69}70 71static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,72			       poll_table *wait)73{74	struct snd_ff *ff = hwdep->private_data;75	__poll_t events;76 77	poll_wait(file, &ff->hwdep_wait, wait);78 79	spin_lock_irq(&ff->lock);80	if (ff->dev_lock_changed || has_msg(ff))81		events = EPOLLIN | EPOLLRDNORM;82	else83		events = 0;84	spin_unlock_irq(&ff->lock);85 86	return events;87}88 89static int hwdep_get_info(struct snd_ff *ff, void __user *arg)90{91	struct fw_device *dev = fw_parent_device(ff->unit);92	struct snd_firewire_get_info info;93 94	memset(&info, 0, sizeof(info));95	info.type = SNDRV_FIREWIRE_TYPE_FIREFACE;96	info.card = dev->card->index;97	*(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);98	*(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);99	strscpy(info.device_name, dev_name(&dev->device),100		sizeof(info.device_name));101 102	if (copy_to_user(arg, &info, sizeof(info)))103		return -EFAULT;104 105	return 0;106}107 108static int hwdep_lock(struct snd_ff *ff)109{110	int err;111 112	spin_lock_irq(&ff->lock);113 114	if (ff->dev_lock_count == 0) {115		ff->dev_lock_count = -1;116		err = 0;117	} else {118		err = -EBUSY;119	}120 121	spin_unlock_irq(&ff->lock);122 123	return err;124}125 126static int hwdep_unlock(struct snd_ff *ff)127{128	int err;129 130	spin_lock_irq(&ff->lock);131 132	if (ff->dev_lock_count == -1) {133		ff->dev_lock_count = 0;134		err = 0;135	} else {136		err = -EBADFD;137	}138 139	spin_unlock_irq(&ff->lock);140 141	return err;142}143 144static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)145{146	struct snd_ff *ff = hwdep->private_data;147 148	spin_lock_irq(&ff->lock);149	if (ff->dev_lock_count == -1)150		ff->dev_lock_count = 0;151	spin_unlock_irq(&ff->lock);152 153	return 0;154}155 156static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,157		       unsigned int cmd, unsigned long arg)158{159	struct snd_ff *ff = hwdep->private_data;160 161	switch (cmd) {162	case SNDRV_FIREWIRE_IOCTL_GET_INFO:163		return hwdep_get_info(ff, (void __user *)arg);164	case SNDRV_FIREWIRE_IOCTL_LOCK:165		return hwdep_lock(ff);166	case SNDRV_FIREWIRE_IOCTL_UNLOCK:167		return hwdep_unlock(ff);168	default:169		return -ENOIOCTLCMD;170	}171}172 173#ifdef CONFIG_COMPAT174static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,175			      unsigned int cmd, unsigned long arg)176{177	return hwdep_ioctl(hwdep, file, cmd,178			   (unsigned long)compat_ptr(arg));179}180#else181#define hwdep_compat_ioctl NULL182#endif183 184int snd_ff_create_hwdep_devices(struct snd_ff *ff)185{186	static const struct snd_hwdep_ops hwdep_ops = {187		.read		= hwdep_read,188		.release	= hwdep_release,189		.poll		= hwdep_poll,190		.ioctl		= hwdep_ioctl,191		.ioctl_compat	= hwdep_compat_ioctl,192	};193	struct snd_hwdep *hwdep;194	int err;195 196	err = snd_hwdep_new(ff->card, ff->card->driver, 0, &hwdep);197	if (err < 0)198		return err;199 200	strcpy(hwdep->name, ff->card->driver);201	hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREFACE;202	hwdep->ops = hwdep_ops;203	hwdep->private_data = ff;204	hwdep->exclusive = true;205 206	return 0;207}208