xref: /linux/sound/firewire/fireface/ff-hwdep.c (revision 34dc1baba215b826e454b8d19e4f24adbeb7d00d)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * ff-hwdep.c - a part of driver for RME Fireface series
4  *
5  * Copyright (c) 2015-2017 Takashi Sakamoto
6  */
7 
8 /*
9  * This codes give three functionality.
10  *
11  * 1.get firewire node information
12  * 2.get notification about starting/stopping stream
13  * 3.lock/unlock stream
14  */
15 
16 #include "ff.h"
17 
18 static bool has_msg(struct snd_ff *ff)
19 {
20 	if (ff->spec->protocol->has_msg)
21 		return ff->spec->protocol->has_msg(ff);
22 	else
23 		return 0;
24 }
25 
26 static 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 the
59 		// 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 
71 static __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 	else
83 		events = 0;
84 	spin_unlock_irq(&ff->lock);
85 
86 	return events;
87 }
88 
89 static 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 
108 static 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 
126 static 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 
144 static 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 
156 static 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_COMPAT
174 static 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 #else
181 #define hwdep_compat_ioctl NULL
182 #endif
183 
184 int 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