xref: /linux/sound/firewire/fireworks/fireworks_hwdep.c (revision a4eb44a6435d6d8f9e642407a4a06f65eb90ca04)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * fireworks_hwdep.c - a part of driver for Fireworks based devices
4  *
5  * Copyright (c) 2013-2014 Takashi Sakamoto
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  * 4.transmit command of EFW transaction
15  * 5.receive response of EFW transaction
16  *
17  */
18 
19 #include "fireworks.h"
20 
21 static long
22 hwdep_read_resp_buf(struct snd_efw *efw, char __user *buf, long remained,
23 		    loff_t *offset)
24 {
25 	unsigned int length, till_end, type;
26 	struct snd_efw_transaction *t;
27 	u8 *pull_ptr;
28 	long count = 0;
29 
30 	if (remained < sizeof(type) + sizeof(struct snd_efw_transaction))
31 		return -ENOSPC;
32 
33 	/* data type is SNDRV_FIREWIRE_EVENT_EFW_RESPONSE */
34 	type = SNDRV_FIREWIRE_EVENT_EFW_RESPONSE;
35 	if (copy_to_user(buf, &type, sizeof(type)))
36 		return -EFAULT;
37 	remained -= sizeof(type);
38 	buf += sizeof(type);
39 
40 	/* write into buffer as many responses as possible */
41 	spin_lock_irq(&efw->lock);
42 
43 	/*
44 	 * When another task reaches here during this task's access to user
45 	 * space, it picks up current position in buffer and can read the same
46 	 * series of responses.
47 	 */
48 	pull_ptr = efw->pull_ptr;
49 
50 	while (efw->push_ptr != pull_ptr) {
51 		t = (struct snd_efw_transaction *)(pull_ptr);
52 		length = be32_to_cpu(t->length) * sizeof(__be32);
53 
54 		/* confirm enough space for this response */
55 		if (remained < length)
56 			break;
57 
58 		/* copy from ring buffer to user buffer */
59 		while (length > 0) {
60 			till_end = snd_efw_resp_buf_size -
61 				(unsigned int)(pull_ptr - efw->resp_buf);
62 			till_end = min_t(unsigned int, length, till_end);
63 
64 			spin_unlock_irq(&efw->lock);
65 
66 			if (copy_to_user(buf, pull_ptr, till_end))
67 				return -EFAULT;
68 
69 			spin_lock_irq(&efw->lock);
70 
71 			pull_ptr += till_end;
72 			if (pull_ptr >= efw->resp_buf + snd_efw_resp_buf_size)
73 				pull_ptr -= snd_efw_resp_buf_size;
74 
75 			length -= till_end;
76 			buf += till_end;
77 			count += till_end;
78 			remained -= till_end;
79 		}
80 	}
81 
82 	/*
83 	 * All of tasks can read from the buffer nearly simultaneously, but the
84 	 * last position for each task is different depending on the length of
85 	 * given buffer. Here, for simplicity, a position of buffer is set by
86 	 * the latest task. It's better for a listening application to allow one
87 	 * thread to read from the buffer. Unless, each task can read different
88 	 * sequence of responses depending on variation of buffer length.
89 	 */
90 	efw->pull_ptr = pull_ptr;
91 
92 	spin_unlock_irq(&efw->lock);
93 
94 	return count;
95 }
96 
97 static long
98 hwdep_read_locked(struct snd_efw *efw, char __user *buf, long count,
99 		  loff_t *offset)
100 {
101 	union snd_firewire_event event = {
102 		.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,
103 	};
104 
105 	spin_lock_irq(&efw->lock);
106 
107 	event.lock_status.status = (efw->dev_lock_count > 0);
108 	efw->dev_lock_changed = false;
109 
110 	spin_unlock_irq(&efw->lock);
111 
112 	count = min_t(long, count, sizeof(event.lock_status));
113 
114 	if (copy_to_user(buf, &event, count))
115 		return -EFAULT;
116 
117 	return count;
118 }
119 
120 static long
121 hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
122 	   loff_t *offset)
123 {
124 	struct snd_efw *efw = hwdep->private_data;
125 	DEFINE_WAIT(wait);
126 	bool dev_lock_changed;
127 	bool queued;
128 
129 	spin_lock_irq(&efw->lock);
130 
131 	dev_lock_changed = efw->dev_lock_changed;
132 	queued = efw->push_ptr != efw->pull_ptr;
133 
134 	while (!dev_lock_changed && !queued) {
135 		prepare_to_wait(&efw->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
136 		spin_unlock_irq(&efw->lock);
137 		schedule();
138 		finish_wait(&efw->hwdep_wait, &wait);
139 		if (signal_pending(current))
140 			return -ERESTARTSYS;
141 		spin_lock_irq(&efw->lock);
142 		dev_lock_changed = efw->dev_lock_changed;
143 		queued = efw->push_ptr != efw->pull_ptr;
144 	}
145 
146 	spin_unlock_irq(&efw->lock);
147 
148 	if (dev_lock_changed)
149 		count = hwdep_read_locked(efw, buf, count, offset);
150 	else if (queued)
151 		count = hwdep_read_resp_buf(efw, buf, count, offset);
152 
153 	return count;
154 }
155 
156 static long
157 hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count,
158 	    loff_t *offset)
159 {
160 	struct snd_efw *efw = hwdep->private_data;
161 	u32 seqnum;
162 	u8 *buf;
163 
164 	if (count < sizeof(struct snd_efw_transaction) ||
165 	    SND_EFW_RESPONSE_MAXIMUM_BYTES < count)
166 		return -EINVAL;
167 
168 	buf = memdup_user(data, count);
169 	if (IS_ERR(buf))
170 		return PTR_ERR(buf);
171 
172 	/* check seqnum is not for kernel-land */
173 	seqnum = be32_to_cpu(((struct snd_efw_transaction *)buf)->seqnum);
174 	if (seqnum > SND_EFW_TRANSACTION_USER_SEQNUM_MAX) {
175 		count = -EINVAL;
176 		goto end;
177 	}
178 
179 	if (snd_efw_transaction_cmd(efw->unit, buf, count) < 0)
180 		count = -EIO;
181 end:
182 	kfree(buf);
183 	return count;
184 }
185 
186 static __poll_t
187 hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait)
188 {
189 	struct snd_efw *efw = hwdep->private_data;
190 	__poll_t events;
191 
192 	poll_wait(file, &efw->hwdep_wait, wait);
193 
194 	spin_lock_irq(&efw->lock);
195 	if (efw->dev_lock_changed || efw->pull_ptr != efw->push_ptr)
196 		events = EPOLLIN | EPOLLRDNORM;
197 	else
198 		events = 0;
199 	spin_unlock_irq(&efw->lock);
200 
201 	return events | EPOLLOUT;
202 }
203 
204 static int
205 hwdep_get_info(struct snd_efw *efw, void __user *arg)
206 {
207 	struct fw_device *dev = fw_parent_device(efw->unit);
208 	struct snd_firewire_get_info info;
209 
210 	memset(&info, 0, sizeof(info));
211 	info.type = SNDRV_FIREWIRE_TYPE_FIREWORKS;
212 	info.card = dev->card->index;
213 	*(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
214 	*(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
215 	strscpy(info.device_name, dev_name(&dev->device),
216 		sizeof(info.device_name));
217 
218 	if (copy_to_user(arg, &info, sizeof(info)))
219 		return -EFAULT;
220 
221 	return 0;
222 }
223 
224 static int
225 hwdep_lock(struct snd_efw *efw)
226 {
227 	int err;
228 
229 	spin_lock_irq(&efw->lock);
230 
231 	if (efw->dev_lock_count == 0) {
232 		efw->dev_lock_count = -1;
233 		err = 0;
234 	} else {
235 		err = -EBUSY;
236 	}
237 
238 	spin_unlock_irq(&efw->lock);
239 
240 	return err;
241 }
242 
243 static int
244 hwdep_unlock(struct snd_efw *efw)
245 {
246 	int err;
247 
248 	spin_lock_irq(&efw->lock);
249 
250 	if (efw->dev_lock_count == -1) {
251 		efw->dev_lock_count = 0;
252 		err = 0;
253 	} else {
254 		err = -EBADFD;
255 	}
256 
257 	spin_unlock_irq(&efw->lock);
258 
259 	return err;
260 }
261 
262 static int
263 hwdep_release(struct snd_hwdep *hwdep, struct file *file)
264 {
265 	struct snd_efw *efw = hwdep->private_data;
266 
267 	spin_lock_irq(&efw->lock);
268 	if (efw->dev_lock_count == -1)
269 		efw->dev_lock_count = 0;
270 	spin_unlock_irq(&efw->lock);
271 
272 	return 0;
273 }
274 
275 static int
276 hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
277 	    unsigned int cmd, unsigned long arg)
278 {
279 	struct snd_efw *efw = hwdep->private_data;
280 
281 	switch (cmd) {
282 	case SNDRV_FIREWIRE_IOCTL_GET_INFO:
283 		return hwdep_get_info(efw, (void __user *)arg);
284 	case SNDRV_FIREWIRE_IOCTL_LOCK:
285 		return hwdep_lock(efw);
286 	case SNDRV_FIREWIRE_IOCTL_UNLOCK:
287 		return hwdep_unlock(efw);
288 	default:
289 		return -ENOIOCTLCMD;
290 	}
291 }
292 
293 #ifdef CONFIG_COMPAT
294 static int
295 hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
296 		   unsigned int cmd, unsigned long arg)
297 {
298 	return hwdep_ioctl(hwdep, file, cmd,
299 			   (unsigned long)compat_ptr(arg));
300 }
301 #else
302 #define hwdep_compat_ioctl NULL
303 #endif
304 
305 int snd_efw_create_hwdep_device(struct snd_efw *efw)
306 {
307 	static const struct snd_hwdep_ops ops = {
308 		.read		= hwdep_read,
309 		.write		= hwdep_write,
310 		.release	= hwdep_release,
311 		.poll		= hwdep_poll,
312 		.ioctl		= hwdep_ioctl,
313 		.ioctl_compat	= hwdep_compat_ioctl,
314 	};
315 	struct snd_hwdep *hwdep;
316 	int err;
317 
318 	err = snd_hwdep_new(efw->card, "Fireworks", 0, &hwdep);
319 	if (err < 0)
320 		goto end;
321 	strcpy(hwdep->name, "Fireworks");
322 	hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREWORKS;
323 	hwdep->ops = ops;
324 	hwdep->private_data = efw;
325 	hwdep->exclusive = true;
326 end:
327 	return err;
328 }
329 
330