xref: /openbmc/linux/sound/firewire/oxfw/oxfw-hwdep.c (revision 00f3a411)
1da607e19SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
28985f4acSTakashi Sakamoto /*
38985f4acSTakashi Sakamoto  * oxfw_hwdep.c - a part of driver for OXFW970/971 based devices
48985f4acSTakashi Sakamoto  *
58985f4acSTakashi Sakamoto  * Copyright (c) 2014 Takashi Sakamoto
68985f4acSTakashi Sakamoto  */
78985f4acSTakashi Sakamoto 
88985f4acSTakashi Sakamoto /*
98985f4acSTakashi Sakamoto  * This codes give three functionality.
108985f4acSTakashi Sakamoto  *
118985f4acSTakashi Sakamoto  * 1.get firewire node information
128985f4acSTakashi Sakamoto  * 2.get notification about starting/stopping stream
138985f4acSTakashi Sakamoto  * 3.lock/unlock stream
148985f4acSTakashi Sakamoto  */
158985f4acSTakashi Sakamoto 
168985f4acSTakashi Sakamoto #include "oxfw.h"
178985f4acSTakashi Sakamoto 
hwdep_read(struct snd_hwdep * hwdep,char __user * buf,long count,loff_t * offset)188985f4acSTakashi Sakamoto static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf,  long count,
198985f4acSTakashi Sakamoto 		       loff_t *offset)
208985f4acSTakashi Sakamoto {
218985f4acSTakashi Sakamoto 	struct snd_oxfw *oxfw = hwdep->private_data;
228985f4acSTakashi Sakamoto 	DEFINE_WAIT(wait);
238985f4acSTakashi Sakamoto 	union snd_firewire_event event;
248985f4acSTakashi Sakamoto 
258985f4acSTakashi Sakamoto 	spin_lock_irq(&oxfw->lock);
268985f4acSTakashi Sakamoto 
278985f4acSTakashi Sakamoto 	while (!oxfw->dev_lock_changed) {
288985f4acSTakashi Sakamoto 		prepare_to_wait(&oxfw->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
298985f4acSTakashi Sakamoto 		spin_unlock_irq(&oxfw->lock);
308985f4acSTakashi Sakamoto 		schedule();
318985f4acSTakashi Sakamoto 		finish_wait(&oxfw->hwdep_wait, &wait);
328985f4acSTakashi Sakamoto 		if (signal_pending(current))
338985f4acSTakashi Sakamoto 			return -ERESTARTSYS;
348985f4acSTakashi Sakamoto 		spin_lock_irq(&oxfw->lock);
358985f4acSTakashi Sakamoto 	}
368985f4acSTakashi Sakamoto 
378985f4acSTakashi Sakamoto 	memset(&event, 0, sizeof(event));
388985f4acSTakashi Sakamoto 	event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
398985f4acSTakashi Sakamoto 	event.lock_status.status = (oxfw->dev_lock_count > 0);
408985f4acSTakashi Sakamoto 	oxfw->dev_lock_changed = false;
418985f4acSTakashi Sakamoto 
428985f4acSTakashi Sakamoto 	count = min_t(long, count, sizeof(event.lock_status));
438985f4acSTakashi Sakamoto 
448985f4acSTakashi Sakamoto 	spin_unlock_irq(&oxfw->lock);
458985f4acSTakashi Sakamoto 
468985f4acSTakashi Sakamoto 	if (copy_to_user(buf, &event, count))
478985f4acSTakashi Sakamoto 		return -EFAULT;
488985f4acSTakashi Sakamoto 
498985f4acSTakashi Sakamoto 	return count;
508985f4acSTakashi Sakamoto }
518985f4acSTakashi Sakamoto 
hwdep_poll(struct snd_hwdep * hwdep,struct file * file,poll_table * wait)52680ef72aSAl Viro static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
538985f4acSTakashi Sakamoto 			       poll_table *wait)
548985f4acSTakashi Sakamoto {
558985f4acSTakashi Sakamoto 	struct snd_oxfw *oxfw = hwdep->private_data;
56680ef72aSAl Viro 	__poll_t events;
578985f4acSTakashi Sakamoto 
588985f4acSTakashi Sakamoto 	poll_wait(file, &oxfw->hwdep_wait, wait);
598985f4acSTakashi Sakamoto 
608985f4acSTakashi Sakamoto 	spin_lock_irq(&oxfw->lock);
618985f4acSTakashi Sakamoto 	if (oxfw->dev_lock_changed)
62a9a08845SLinus Torvalds 		events = EPOLLIN | EPOLLRDNORM;
638985f4acSTakashi Sakamoto 	else
648985f4acSTakashi Sakamoto 		events = 0;
658985f4acSTakashi Sakamoto 	spin_unlock_irq(&oxfw->lock);
668985f4acSTakashi Sakamoto 
678985f4acSTakashi Sakamoto 	return events;
688985f4acSTakashi Sakamoto }
698985f4acSTakashi Sakamoto 
hwdep_get_info(struct snd_oxfw * oxfw,void __user * arg)708985f4acSTakashi Sakamoto static int hwdep_get_info(struct snd_oxfw *oxfw, void __user *arg)
718985f4acSTakashi Sakamoto {
728985f4acSTakashi Sakamoto 	struct fw_device *dev = fw_parent_device(oxfw->unit);
738985f4acSTakashi Sakamoto 	struct snd_firewire_get_info info;
748985f4acSTakashi Sakamoto 
758985f4acSTakashi Sakamoto 	memset(&info, 0, sizeof(info));
768985f4acSTakashi Sakamoto 	info.type = SNDRV_FIREWIRE_TYPE_OXFW;
778985f4acSTakashi Sakamoto 	info.card = dev->card->index;
788985f4acSTakashi Sakamoto 	*(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
798985f4acSTakashi Sakamoto 	*(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
80*75b1a8f9SJoe Perches 	strscpy(info.device_name, dev_name(&dev->device),
818985f4acSTakashi Sakamoto 		sizeof(info.device_name));
828985f4acSTakashi Sakamoto 
838985f4acSTakashi Sakamoto 	if (copy_to_user(arg, &info, sizeof(info)))
848985f4acSTakashi Sakamoto 		return -EFAULT;
858985f4acSTakashi Sakamoto 
868985f4acSTakashi Sakamoto 	return 0;
878985f4acSTakashi Sakamoto }
888985f4acSTakashi Sakamoto 
hwdep_lock(struct snd_oxfw * oxfw)898985f4acSTakashi Sakamoto static int hwdep_lock(struct snd_oxfw *oxfw)
908985f4acSTakashi Sakamoto {
918985f4acSTakashi Sakamoto 	int err;
928985f4acSTakashi Sakamoto 
938985f4acSTakashi Sakamoto 	spin_lock_irq(&oxfw->lock);
948985f4acSTakashi Sakamoto 
958985f4acSTakashi Sakamoto 	if (oxfw->dev_lock_count == 0) {
968985f4acSTakashi Sakamoto 		oxfw->dev_lock_count = -1;
978985f4acSTakashi Sakamoto 		err = 0;
988985f4acSTakashi Sakamoto 	} else {
998985f4acSTakashi Sakamoto 		err = -EBUSY;
1008985f4acSTakashi Sakamoto 	}
1018985f4acSTakashi Sakamoto 
1028985f4acSTakashi Sakamoto 	spin_unlock_irq(&oxfw->lock);
1038985f4acSTakashi Sakamoto 
1048985f4acSTakashi Sakamoto 	return err;
1058985f4acSTakashi Sakamoto }
1068985f4acSTakashi Sakamoto 
hwdep_unlock(struct snd_oxfw * oxfw)1078985f4acSTakashi Sakamoto static int hwdep_unlock(struct snd_oxfw *oxfw)
1088985f4acSTakashi Sakamoto {
1098985f4acSTakashi Sakamoto 	int err;
1108985f4acSTakashi Sakamoto 
1118985f4acSTakashi Sakamoto 	spin_lock_irq(&oxfw->lock);
1128985f4acSTakashi Sakamoto 
1138985f4acSTakashi Sakamoto 	if (oxfw->dev_lock_count == -1) {
1148985f4acSTakashi Sakamoto 		oxfw->dev_lock_count = 0;
1158985f4acSTakashi Sakamoto 		err = 0;
1168985f4acSTakashi Sakamoto 	} else {
1178985f4acSTakashi Sakamoto 		err = -EBADFD;
1188985f4acSTakashi Sakamoto 	}
1198985f4acSTakashi Sakamoto 
1208985f4acSTakashi Sakamoto 	spin_unlock_irq(&oxfw->lock);
1218985f4acSTakashi Sakamoto 
1228985f4acSTakashi Sakamoto 	return err;
1238985f4acSTakashi Sakamoto }
1248985f4acSTakashi Sakamoto 
hwdep_release(struct snd_hwdep * hwdep,struct file * file)1258985f4acSTakashi Sakamoto static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
1268985f4acSTakashi Sakamoto {
1278985f4acSTakashi Sakamoto 	struct snd_oxfw *oxfw = hwdep->private_data;
1288985f4acSTakashi Sakamoto 
1298985f4acSTakashi Sakamoto 	spin_lock_irq(&oxfw->lock);
1308985f4acSTakashi Sakamoto 	if (oxfw->dev_lock_count == -1)
1318985f4acSTakashi Sakamoto 		oxfw->dev_lock_count = 0;
1328985f4acSTakashi Sakamoto 	spin_unlock_irq(&oxfw->lock);
1338985f4acSTakashi Sakamoto 
1348985f4acSTakashi Sakamoto 	return 0;
1358985f4acSTakashi Sakamoto }
1368985f4acSTakashi Sakamoto 
hwdep_ioctl(struct snd_hwdep * hwdep,struct file * file,unsigned int cmd,unsigned long arg)1378985f4acSTakashi Sakamoto static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
1388985f4acSTakashi Sakamoto 		       unsigned int cmd, unsigned long arg)
1398985f4acSTakashi Sakamoto {
1408985f4acSTakashi Sakamoto 	struct snd_oxfw *oxfw = hwdep->private_data;
1418985f4acSTakashi Sakamoto 
1428985f4acSTakashi Sakamoto 	switch (cmd) {
1438985f4acSTakashi Sakamoto 	case SNDRV_FIREWIRE_IOCTL_GET_INFO:
1448985f4acSTakashi Sakamoto 		return hwdep_get_info(oxfw, (void __user *)arg);
1458985f4acSTakashi Sakamoto 	case SNDRV_FIREWIRE_IOCTL_LOCK:
1468985f4acSTakashi Sakamoto 		return hwdep_lock(oxfw);
1478985f4acSTakashi Sakamoto 	case SNDRV_FIREWIRE_IOCTL_UNLOCK:
1488985f4acSTakashi Sakamoto 		return hwdep_unlock(oxfw);
1498985f4acSTakashi Sakamoto 	default:
1508985f4acSTakashi Sakamoto 		return -ENOIOCTLCMD;
1518985f4acSTakashi Sakamoto 	}
1528985f4acSTakashi Sakamoto }
1538985f4acSTakashi Sakamoto 
1548985f4acSTakashi Sakamoto #ifdef CONFIG_COMPAT
hwdep_compat_ioctl(struct snd_hwdep * hwdep,struct file * file,unsigned int cmd,unsigned long arg)1558985f4acSTakashi Sakamoto static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
1568985f4acSTakashi Sakamoto 			      unsigned int cmd, unsigned long arg)
1578985f4acSTakashi Sakamoto {
1588985f4acSTakashi Sakamoto 	return hwdep_ioctl(hwdep, file, cmd,
1598985f4acSTakashi Sakamoto 			   (unsigned long)compat_ptr(arg));
1608985f4acSTakashi Sakamoto }
1618985f4acSTakashi Sakamoto #else
1628985f4acSTakashi Sakamoto #define hwdep_compat_ioctl NULL
1638985f4acSTakashi Sakamoto #endif
1648985f4acSTakashi Sakamoto 
snd_oxfw_create_hwdep(struct snd_oxfw * oxfw)1658985f4acSTakashi Sakamoto int snd_oxfw_create_hwdep(struct snd_oxfw *oxfw)
1668985f4acSTakashi Sakamoto {
1678985f4acSTakashi Sakamoto 	static const struct snd_hwdep_ops hwdep_ops = {
1688985f4acSTakashi Sakamoto 		.read		= hwdep_read,
1698985f4acSTakashi Sakamoto 		.release	= hwdep_release,
1708985f4acSTakashi Sakamoto 		.poll		= hwdep_poll,
1718985f4acSTakashi Sakamoto 		.ioctl		= hwdep_ioctl,
1728985f4acSTakashi Sakamoto 		.ioctl_compat	= hwdep_compat_ioctl,
1738985f4acSTakashi Sakamoto 	};
1748985f4acSTakashi Sakamoto 	struct snd_hwdep *hwdep;
1758985f4acSTakashi Sakamoto 	int err;
1768985f4acSTakashi Sakamoto 
1778985f4acSTakashi Sakamoto 	err = snd_hwdep_new(oxfw->card, oxfw->card->driver, 0, &hwdep);
1788985f4acSTakashi Sakamoto 	if (err < 0)
1798985f4acSTakashi Sakamoto 		goto end;
1808985f4acSTakashi Sakamoto 	strcpy(hwdep->name, oxfw->card->driver);
1818985f4acSTakashi Sakamoto 	hwdep->iface = SNDRV_HWDEP_IFACE_FW_OXFW;
1828985f4acSTakashi Sakamoto 	hwdep->ops = hwdep_ops;
1838985f4acSTakashi Sakamoto 	hwdep->private_data = oxfw;
1848985f4acSTakashi Sakamoto 	hwdep->exclusive = true;
1858985f4acSTakashi Sakamoto end:
1868985f4acSTakashi Sakamoto 	return err;
1878985f4acSTakashi Sakamoto }
188