xref: /openbmc/linux/sound/firewire/digi00x/digi00x-hwdep.c (revision d0034a7a4ac7fae708146ac0059b9c47a1543f0d)
1da607e19SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2660dd3d5STakashi Sakamoto /*
3660dd3d5STakashi Sakamoto  * digi00x-hwdep.c - a part of driver for Digidesign Digi 002/003 family
4660dd3d5STakashi Sakamoto  *
5660dd3d5STakashi Sakamoto  * Copyright (c) 2014-2015 Takashi Sakamoto
6660dd3d5STakashi Sakamoto  */
7660dd3d5STakashi Sakamoto 
8660dd3d5STakashi Sakamoto /*
9660dd3d5STakashi Sakamoto  * This codes give three functionality.
10660dd3d5STakashi Sakamoto  *
11660dd3d5STakashi Sakamoto  * 1.get firewire node information
12660dd3d5STakashi Sakamoto  * 2.get notification about starting/stopping stream
13660dd3d5STakashi Sakamoto  * 3.lock/unlock stream
1444b73088STakashi Sakamoto  * 4.get asynchronous messaging
15660dd3d5STakashi Sakamoto  */
16660dd3d5STakashi Sakamoto 
17660dd3d5STakashi Sakamoto #include "digi00x.h"
18660dd3d5STakashi Sakamoto 
hwdep_read(struct snd_hwdep * hwdep,char __user * buf,long count,loff_t * offset)19660dd3d5STakashi Sakamoto static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf,  long count,
20660dd3d5STakashi Sakamoto 		       loff_t *offset)
21660dd3d5STakashi Sakamoto {
22660dd3d5STakashi Sakamoto 	struct snd_dg00x *dg00x = hwdep->private_data;
23660dd3d5STakashi Sakamoto 	DEFINE_WAIT(wait);
24660dd3d5STakashi Sakamoto 	union snd_firewire_event event;
25660dd3d5STakashi Sakamoto 
26660dd3d5STakashi Sakamoto 	spin_lock_irq(&dg00x->lock);
27660dd3d5STakashi Sakamoto 
2844b73088STakashi Sakamoto 	while (!dg00x->dev_lock_changed && dg00x->msg == 0) {
29660dd3d5STakashi Sakamoto 		prepare_to_wait(&dg00x->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
30660dd3d5STakashi Sakamoto 		spin_unlock_irq(&dg00x->lock);
31660dd3d5STakashi Sakamoto 		schedule();
32660dd3d5STakashi Sakamoto 		finish_wait(&dg00x->hwdep_wait, &wait);
33660dd3d5STakashi Sakamoto 		if (signal_pending(current))
34660dd3d5STakashi Sakamoto 			return -ERESTARTSYS;
35660dd3d5STakashi Sakamoto 		spin_lock_irq(&dg00x->lock);
36660dd3d5STakashi Sakamoto 	}
37660dd3d5STakashi Sakamoto 
38660dd3d5STakashi Sakamoto 	memset(&event, 0, sizeof(event));
39660dd3d5STakashi Sakamoto 	if (dg00x->dev_lock_changed) {
40660dd3d5STakashi Sakamoto 		event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
41660dd3d5STakashi Sakamoto 		event.lock_status.status = (dg00x->dev_lock_count > 0);
42660dd3d5STakashi Sakamoto 		dg00x->dev_lock_changed = false;
43660dd3d5STakashi Sakamoto 
44660dd3d5STakashi Sakamoto 		count = min_t(long, count, sizeof(event.lock_status));
4544b73088STakashi Sakamoto 	} else {
4644b73088STakashi Sakamoto 		event.digi00x_message.type =
4744b73088STakashi Sakamoto 					SNDRV_FIREWIRE_EVENT_DIGI00X_MESSAGE;
4844b73088STakashi Sakamoto 		event.digi00x_message.message = dg00x->msg;
4944b73088STakashi Sakamoto 		dg00x->msg = 0;
5044b73088STakashi Sakamoto 
5144b73088STakashi Sakamoto 		count = min_t(long, count, sizeof(event.digi00x_message));
52660dd3d5STakashi Sakamoto 	}
53660dd3d5STakashi Sakamoto 
54660dd3d5STakashi Sakamoto 	spin_unlock_irq(&dg00x->lock);
55660dd3d5STakashi Sakamoto 
56660dd3d5STakashi Sakamoto 	if (copy_to_user(buf, &event, count))
57660dd3d5STakashi Sakamoto 		return -EFAULT;
58660dd3d5STakashi Sakamoto 
59660dd3d5STakashi Sakamoto 	return count;
60660dd3d5STakashi Sakamoto }
61660dd3d5STakashi Sakamoto 
hwdep_poll(struct snd_hwdep * hwdep,struct file * file,poll_table * wait)62680ef72aSAl Viro static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
63660dd3d5STakashi Sakamoto 			       poll_table *wait)
64660dd3d5STakashi Sakamoto {
65660dd3d5STakashi Sakamoto 	struct snd_dg00x *dg00x = hwdep->private_data;
66680ef72aSAl Viro 	__poll_t events;
67660dd3d5STakashi Sakamoto 
68660dd3d5STakashi Sakamoto 	poll_wait(file, &dg00x->hwdep_wait, wait);
69660dd3d5STakashi Sakamoto 
70660dd3d5STakashi Sakamoto 	spin_lock_irq(&dg00x->lock);
7144b73088STakashi Sakamoto 	if (dg00x->dev_lock_changed || dg00x->msg)
72a9a08845SLinus Torvalds 		events = EPOLLIN | EPOLLRDNORM;
73660dd3d5STakashi Sakamoto 	else
74660dd3d5STakashi Sakamoto 		events = 0;
75660dd3d5STakashi Sakamoto 	spin_unlock_irq(&dg00x->lock);
76660dd3d5STakashi Sakamoto 
77660dd3d5STakashi Sakamoto 	return events;
78660dd3d5STakashi Sakamoto }
79660dd3d5STakashi Sakamoto 
hwdep_get_info(struct snd_dg00x * dg00x,void __user * arg)80660dd3d5STakashi Sakamoto static int hwdep_get_info(struct snd_dg00x *dg00x, void __user *arg)
81660dd3d5STakashi Sakamoto {
82660dd3d5STakashi Sakamoto 	struct fw_device *dev = fw_parent_device(dg00x->unit);
83660dd3d5STakashi Sakamoto 	struct snd_firewire_get_info info;
84660dd3d5STakashi Sakamoto 
85660dd3d5STakashi Sakamoto 	memset(&info, 0, sizeof(info));
86660dd3d5STakashi Sakamoto 	info.type = SNDRV_FIREWIRE_TYPE_DIGI00X;
87660dd3d5STakashi Sakamoto 	info.card = dev->card->index;
88660dd3d5STakashi Sakamoto 	*(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
89660dd3d5STakashi Sakamoto 	*(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
90*75b1a8f9SJoe Perches 	strscpy(info.device_name, dev_name(&dev->device),
91660dd3d5STakashi Sakamoto 		sizeof(info.device_name));
92660dd3d5STakashi Sakamoto 
93660dd3d5STakashi Sakamoto 	if (copy_to_user(arg, &info, sizeof(info)))
94660dd3d5STakashi Sakamoto 		return -EFAULT;
95660dd3d5STakashi Sakamoto 
96660dd3d5STakashi Sakamoto 	return 0;
97660dd3d5STakashi Sakamoto }
98660dd3d5STakashi Sakamoto 
hwdep_lock(struct snd_dg00x * dg00x)99660dd3d5STakashi Sakamoto static int hwdep_lock(struct snd_dg00x *dg00x)
100660dd3d5STakashi Sakamoto {
101660dd3d5STakashi Sakamoto 	int err;
102660dd3d5STakashi Sakamoto 
103660dd3d5STakashi Sakamoto 	spin_lock_irq(&dg00x->lock);
104660dd3d5STakashi Sakamoto 
105660dd3d5STakashi Sakamoto 	if (dg00x->dev_lock_count == 0) {
106660dd3d5STakashi Sakamoto 		dg00x->dev_lock_count = -1;
107660dd3d5STakashi Sakamoto 		err = 0;
108660dd3d5STakashi Sakamoto 	} else {
109660dd3d5STakashi Sakamoto 		err = -EBUSY;
110660dd3d5STakashi Sakamoto 	}
111660dd3d5STakashi Sakamoto 
112660dd3d5STakashi Sakamoto 	spin_unlock_irq(&dg00x->lock);
113660dd3d5STakashi Sakamoto 
114660dd3d5STakashi Sakamoto 	return err;
115660dd3d5STakashi Sakamoto }
116660dd3d5STakashi Sakamoto 
hwdep_unlock(struct snd_dg00x * dg00x)117660dd3d5STakashi Sakamoto static int hwdep_unlock(struct snd_dg00x *dg00x)
118660dd3d5STakashi Sakamoto {
119660dd3d5STakashi Sakamoto 	int err;
120660dd3d5STakashi Sakamoto 
121660dd3d5STakashi Sakamoto 	spin_lock_irq(&dg00x->lock);
122660dd3d5STakashi Sakamoto 
123660dd3d5STakashi Sakamoto 	if (dg00x->dev_lock_count == -1) {
124660dd3d5STakashi Sakamoto 		dg00x->dev_lock_count = 0;
125660dd3d5STakashi Sakamoto 		err = 0;
126660dd3d5STakashi Sakamoto 	} else {
127660dd3d5STakashi Sakamoto 		err = -EBADFD;
128660dd3d5STakashi Sakamoto 	}
129660dd3d5STakashi Sakamoto 
130660dd3d5STakashi Sakamoto 	spin_unlock_irq(&dg00x->lock);
131660dd3d5STakashi Sakamoto 
132660dd3d5STakashi Sakamoto 	return err;
133660dd3d5STakashi Sakamoto }
134660dd3d5STakashi Sakamoto 
hwdep_release(struct snd_hwdep * hwdep,struct file * file)135660dd3d5STakashi Sakamoto static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
136660dd3d5STakashi Sakamoto {
137660dd3d5STakashi Sakamoto 	struct snd_dg00x *dg00x = hwdep->private_data;
138660dd3d5STakashi Sakamoto 
139660dd3d5STakashi Sakamoto 	spin_lock_irq(&dg00x->lock);
140660dd3d5STakashi Sakamoto 	if (dg00x->dev_lock_count == -1)
141660dd3d5STakashi Sakamoto 		dg00x->dev_lock_count = 0;
142660dd3d5STakashi Sakamoto 	spin_unlock_irq(&dg00x->lock);
143660dd3d5STakashi Sakamoto 
144660dd3d5STakashi Sakamoto 	return 0;
145660dd3d5STakashi Sakamoto }
146660dd3d5STakashi Sakamoto 
hwdep_ioctl(struct snd_hwdep * hwdep,struct file * file,unsigned int cmd,unsigned long arg)147660dd3d5STakashi Sakamoto static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
148660dd3d5STakashi Sakamoto 	    unsigned int cmd, unsigned long arg)
149660dd3d5STakashi Sakamoto {
150660dd3d5STakashi Sakamoto 	struct snd_dg00x *dg00x = hwdep->private_data;
151660dd3d5STakashi Sakamoto 
152660dd3d5STakashi Sakamoto 	switch (cmd) {
153660dd3d5STakashi Sakamoto 	case SNDRV_FIREWIRE_IOCTL_GET_INFO:
154660dd3d5STakashi Sakamoto 		return hwdep_get_info(dg00x, (void __user *)arg);
155660dd3d5STakashi Sakamoto 	case SNDRV_FIREWIRE_IOCTL_LOCK:
156660dd3d5STakashi Sakamoto 		return hwdep_lock(dg00x);
157660dd3d5STakashi Sakamoto 	case SNDRV_FIREWIRE_IOCTL_UNLOCK:
158660dd3d5STakashi Sakamoto 		return hwdep_unlock(dg00x);
159660dd3d5STakashi Sakamoto 	default:
160660dd3d5STakashi Sakamoto 		return -ENOIOCTLCMD;
161660dd3d5STakashi Sakamoto 	}
162660dd3d5STakashi Sakamoto }
163660dd3d5STakashi Sakamoto 
164660dd3d5STakashi Sakamoto #ifdef CONFIG_COMPAT
hwdep_compat_ioctl(struct snd_hwdep * hwdep,struct file * file,unsigned int cmd,unsigned long arg)165660dd3d5STakashi Sakamoto static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
166660dd3d5STakashi Sakamoto 			      unsigned int cmd, unsigned long arg)
167660dd3d5STakashi Sakamoto {
168660dd3d5STakashi Sakamoto 	return hwdep_ioctl(hwdep, file, cmd,
169660dd3d5STakashi Sakamoto 			   (unsigned long)compat_ptr(arg));
170660dd3d5STakashi Sakamoto }
171660dd3d5STakashi Sakamoto #else
172660dd3d5STakashi Sakamoto #define hwdep_compat_ioctl NULL
173660dd3d5STakashi Sakamoto #endif
174660dd3d5STakashi Sakamoto 
snd_dg00x_create_hwdep_device(struct snd_dg00x * dg00x)175a4e86cbaSTakashi Sakamoto int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x)
176a4e86cbaSTakashi Sakamoto {
177a4e86cbaSTakashi Sakamoto 	static const struct snd_hwdep_ops ops = {
178660dd3d5STakashi Sakamoto 		.read		= hwdep_read,
179660dd3d5STakashi Sakamoto 		.release	= hwdep_release,
180660dd3d5STakashi Sakamoto 		.poll		= hwdep_poll,
181660dd3d5STakashi Sakamoto 		.ioctl		= hwdep_ioctl,
182660dd3d5STakashi Sakamoto 		.ioctl_compat	= hwdep_compat_ioctl,
183660dd3d5STakashi Sakamoto 	};
184660dd3d5STakashi Sakamoto 	struct snd_hwdep *hwdep;
185660dd3d5STakashi Sakamoto 	int err;
186660dd3d5STakashi Sakamoto 
187660dd3d5STakashi Sakamoto 	err = snd_hwdep_new(dg00x->card, "Digi00x", 0, &hwdep);
188660dd3d5STakashi Sakamoto 	if (err < 0)
189660dd3d5STakashi Sakamoto 		return err;
190660dd3d5STakashi Sakamoto 
191660dd3d5STakashi Sakamoto 	strcpy(hwdep->name, "Digi00x");
192660dd3d5STakashi Sakamoto 	hwdep->iface = SNDRV_HWDEP_IFACE_FW_DIGI00X;
193a4e86cbaSTakashi Sakamoto 	hwdep->ops = ops;
194660dd3d5STakashi Sakamoto 	hwdep->private_data = dg00x;
195660dd3d5STakashi Sakamoto 	hwdep->exclusive = true;
196660dd3d5STakashi Sakamoto 
197660dd3d5STakashi Sakamoto 	return err;
198660dd3d5STakashi Sakamoto }
199