xref: /OK3568_Linux_fs/kernel/sound/firewire/tascam/tascam-hwdep.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * tascam-hwdep.c - a part of driver for TASCAM FireWire series
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (c) 2015 Takashi Sakamoto
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun /*
9*4882a593Smuzhiyun  * This codes give three functionality.
10*4882a593Smuzhiyun  *
11*4882a593Smuzhiyun  * 1.get firewire node information
12*4882a593Smuzhiyun  * 2.get notification about starting/stopping stream
13*4882a593Smuzhiyun  * 3.lock/unlock stream
14*4882a593Smuzhiyun  */
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun #include "tascam.h"
17*4882a593Smuzhiyun 
tscm_hwdep_read_locked(struct snd_tscm * tscm,char __user * buf,long count,loff_t * offset)18*4882a593Smuzhiyun static long tscm_hwdep_read_locked(struct snd_tscm *tscm, char __user *buf,
19*4882a593Smuzhiyun 				   long count, loff_t *offset)
20*4882a593Smuzhiyun 	__releases(&tscm->lock)
21*4882a593Smuzhiyun {
22*4882a593Smuzhiyun 	struct snd_firewire_event_lock_status event = {
23*4882a593Smuzhiyun 		.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,
24*4882a593Smuzhiyun 	};
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun 	event.status = (tscm->dev_lock_count > 0);
27*4882a593Smuzhiyun 	tscm->dev_lock_changed = false;
28*4882a593Smuzhiyun 	count = min_t(long, count, sizeof(event));
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun 	spin_unlock_irq(&tscm->lock);
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun 	if (copy_to_user(buf, &event, count))
33*4882a593Smuzhiyun 		return -EFAULT;
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun 	return count;
36*4882a593Smuzhiyun }
37*4882a593Smuzhiyun 
tscm_hwdep_read_queue(struct snd_tscm * tscm,char __user * buf,long remained,loff_t * offset)38*4882a593Smuzhiyun static long tscm_hwdep_read_queue(struct snd_tscm *tscm, char __user *buf,
39*4882a593Smuzhiyun 				  long remained, loff_t *offset)
40*4882a593Smuzhiyun 	__releases(&tscm->lock)
41*4882a593Smuzhiyun {
42*4882a593Smuzhiyun 	char __user *pos = buf;
43*4882a593Smuzhiyun 	unsigned int type = SNDRV_FIREWIRE_EVENT_TASCAM_CONTROL;
44*4882a593Smuzhiyun 	struct snd_firewire_tascam_change *entries = tscm->queue;
45*4882a593Smuzhiyun 	long count;
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun 	// At least, one control event can be copied.
48*4882a593Smuzhiyun 	if (remained < sizeof(type) + sizeof(*entries)) {
49*4882a593Smuzhiyun 		spin_unlock_irq(&tscm->lock);
50*4882a593Smuzhiyun 		return -EINVAL;
51*4882a593Smuzhiyun 	}
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun 	// Copy the type field later.
54*4882a593Smuzhiyun 	count = sizeof(type);
55*4882a593Smuzhiyun 	remained -= sizeof(type);
56*4882a593Smuzhiyun 	pos += sizeof(type);
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun 	while (true) {
59*4882a593Smuzhiyun 		unsigned int head_pos;
60*4882a593Smuzhiyun 		unsigned int tail_pos;
61*4882a593Smuzhiyun 		unsigned int length;
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun 		if (tscm->pull_pos == tscm->push_pos)
64*4882a593Smuzhiyun 			break;
65*4882a593Smuzhiyun 		else if (tscm->pull_pos < tscm->push_pos)
66*4882a593Smuzhiyun 			tail_pos = tscm->push_pos;
67*4882a593Smuzhiyun 		else
68*4882a593Smuzhiyun 			tail_pos = SND_TSCM_QUEUE_COUNT;
69*4882a593Smuzhiyun 		head_pos = tscm->pull_pos;
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun 		length = (tail_pos - head_pos) * sizeof(*entries);
72*4882a593Smuzhiyun 		if (remained < length)
73*4882a593Smuzhiyun 			length = rounddown(remained, sizeof(*entries));
74*4882a593Smuzhiyun 		if (length == 0)
75*4882a593Smuzhiyun 			break;
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun 		spin_unlock_irq(&tscm->lock);
78*4882a593Smuzhiyun 		if (copy_to_user(pos, &entries[head_pos], length))
79*4882a593Smuzhiyun 			return -EFAULT;
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun 		spin_lock_irq(&tscm->lock);
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun 		tscm->pull_pos = tail_pos % SND_TSCM_QUEUE_COUNT;
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 		count += length;
86*4882a593Smuzhiyun 		remained -= length;
87*4882a593Smuzhiyun 		pos += length;
88*4882a593Smuzhiyun 	}
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun 	spin_unlock_irq(&tscm->lock);
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	if (copy_to_user(buf, &type, sizeof(type)))
93*4882a593Smuzhiyun 		return -EFAULT;
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	return count;
96*4882a593Smuzhiyun }
97*4882a593Smuzhiyun 
hwdep_read(struct snd_hwdep * hwdep,char __user * buf,long count,loff_t * offset)98*4882a593Smuzhiyun static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
99*4882a593Smuzhiyun 		       loff_t *offset)
100*4882a593Smuzhiyun {
101*4882a593Smuzhiyun 	struct snd_tscm *tscm = hwdep->private_data;
102*4882a593Smuzhiyun 	DEFINE_WAIT(wait);
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 	spin_lock_irq(&tscm->lock);
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	while (!tscm->dev_lock_changed && tscm->push_pos == tscm->pull_pos) {
107*4882a593Smuzhiyun 		prepare_to_wait(&tscm->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
108*4882a593Smuzhiyun 		spin_unlock_irq(&tscm->lock);
109*4882a593Smuzhiyun 		schedule();
110*4882a593Smuzhiyun 		finish_wait(&tscm->hwdep_wait, &wait);
111*4882a593Smuzhiyun 		if (signal_pending(current))
112*4882a593Smuzhiyun 			return -ERESTARTSYS;
113*4882a593Smuzhiyun 		spin_lock_irq(&tscm->lock);
114*4882a593Smuzhiyun 	}
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 	// NOTE: The acquired lock should be released in callee side.
117*4882a593Smuzhiyun 	if (tscm->dev_lock_changed) {
118*4882a593Smuzhiyun 		count = tscm_hwdep_read_locked(tscm, buf, count, offset);
119*4882a593Smuzhiyun 	} else if (tscm->push_pos != tscm->pull_pos) {
120*4882a593Smuzhiyun 		count = tscm_hwdep_read_queue(tscm, buf, count, offset);
121*4882a593Smuzhiyun 	} else {
122*4882a593Smuzhiyun 		spin_unlock_irq(&tscm->lock);
123*4882a593Smuzhiyun 		count = 0;
124*4882a593Smuzhiyun 	}
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	return count;
127*4882a593Smuzhiyun }
128*4882a593Smuzhiyun 
hwdep_poll(struct snd_hwdep * hwdep,struct file * file,poll_table * wait)129*4882a593Smuzhiyun static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
130*4882a593Smuzhiyun 			       poll_table *wait)
131*4882a593Smuzhiyun {
132*4882a593Smuzhiyun 	struct snd_tscm *tscm = hwdep->private_data;
133*4882a593Smuzhiyun 	__poll_t events;
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun 	poll_wait(file, &tscm->hwdep_wait, wait);
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	spin_lock_irq(&tscm->lock);
138*4882a593Smuzhiyun 	if (tscm->dev_lock_changed || tscm->push_pos != tscm->pull_pos)
139*4882a593Smuzhiyun 		events = EPOLLIN | EPOLLRDNORM;
140*4882a593Smuzhiyun 	else
141*4882a593Smuzhiyun 		events = 0;
142*4882a593Smuzhiyun 	spin_unlock_irq(&tscm->lock);
143*4882a593Smuzhiyun 
144*4882a593Smuzhiyun 	return events;
145*4882a593Smuzhiyun }
146*4882a593Smuzhiyun 
hwdep_get_info(struct snd_tscm * tscm,void __user * arg)147*4882a593Smuzhiyun static int hwdep_get_info(struct snd_tscm *tscm, void __user *arg)
148*4882a593Smuzhiyun {
149*4882a593Smuzhiyun 	struct fw_device *dev = fw_parent_device(tscm->unit);
150*4882a593Smuzhiyun 	struct snd_firewire_get_info info;
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun 	memset(&info, 0, sizeof(info));
153*4882a593Smuzhiyun 	info.type = SNDRV_FIREWIRE_TYPE_TASCAM;
154*4882a593Smuzhiyun 	info.card = dev->card->index;
155*4882a593Smuzhiyun 	*(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
156*4882a593Smuzhiyun 	*(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
157*4882a593Smuzhiyun 	strlcpy(info.device_name, dev_name(&dev->device),
158*4882a593Smuzhiyun 		sizeof(info.device_name));
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 	if (copy_to_user(arg, &info, sizeof(info)))
161*4882a593Smuzhiyun 		return -EFAULT;
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	return 0;
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun 
hwdep_lock(struct snd_tscm * tscm)166*4882a593Smuzhiyun static int hwdep_lock(struct snd_tscm *tscm)
167*4882a593Smuzhiyun {
168*4882a593Smuzhiyun 	int err;
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 	spin_lock_irq(&tscm->lock);
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun 	if (tscm->dev_lock_count == 0) {
173*4882a593Smuzhiyun 		tscm->dev_lock_count = -1;
174*4882a593Smuzhiyun 		err = 0;
175*4882a593Smuzhiyun 	} else {
176*4882a593Smuzhiyun 		err = -EBUSY;
177*4882a593Smuzhiyun 	}
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 	spin_unlock_irq(&tscm->lock);
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 	return err;
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun 
hwdep_unlock(struct snd_tscm * tscm)184*4882a593Smuzhiyun static int hwdep_unlock(struct snd_tscm *tscm)
185*4882a593Smuzhiyun {
186*4882a593Smuzhiyun 	int err;
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 	spin_lock_irq(&tscm->lock);
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun 	if (tscm->dev_lock_count == -1) {
191*4882a593Smuzhiyun 		tscm->dev_lock_count = 0;
192*4882a593Smuzhiyun 		err = 0;
193*4882a593Smuzhiyun 	} else {
194*4882a593Smuzhiyun 		err = -EBADFD;
195*4882a593Smuzhiyun 	}
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 	spin_unlock_irq(&tscm->lock);
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun 	return err;
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun 
tscm_hwdep_state(struct snd_tscm * tscm,void __user * arg)202*4882a593Smuzhiyun static int tscm_hwdep_state(struct snd_tscm *tscm, void __user *arg)
203*4882a593Smuzhiyun {
204*4882a593Smuzhiyun 	if (copy_to_user(arg, tscm->state, sizeof(tscm->state)))
205*4882a593Smuzhiyun 		return -EFAULT;
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun 	return 0;
208*4882a593Smuzhiyun }
209*4882a593Smuzhiyun 
hwdep_release(struct snd_hwdep * hwdep,struct file * file)210*4882a593Smuzhiyun static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
211*4882a593Smuzhiyun {
212*4882a593Smuzhiyun 	struct snd_tscm *tscm = hwdep->private_data;
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 	spin_lock_irq(&tscm->lock);
215*4882a593Smuzhiyun 	if (tscm->dev_lock_count == -1)
216*4882a593Smuzhiyun 		tscm->dev_lock_count = 0;
217*4882a593Smuzhiyun 	spin_unlock_irq(&tscm->lock);
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	return 0;
220*4882a593Smuzhiyun }
221*4882a593Smuzhiyun 
hwdep_ioctl(struct snd_hwdep * hwdep,struct file * file,unsigned int cmd,unsigned long arg)222*4882a593Smuzhiyun static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
223*4882a593Smuzhiyun 	    unsigned int cmd, unsigned long arg)
224*4882a593Smuzhiyun {
225*4882a593Smuzhiyun 	struct snd_tscm *tscm = hwdep->private_data;
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	switch (cmd) {
228*4882a593Smuzhiyun 	case SNDRV_FIREWIRE_IOCTL_GET_INFO:
229*4882a593Smuzhiyun 		return hwdep_get_info(tscm, (void __user *)arg);
230*4882a593Smuzhiyun 	case SNDRV_FIREWIRE_IOCTL_LOCK:
231*4882a593Smuzhiyun 		return hwdep_lock(tscm);
232*4882a593Smuzhiyun 	case SNDRV_FIREWIRE_IOCTL_UNLOCK:
233*4882a593Smuzhiyun 		return hwdep_unlock(tscm);
234*4882a593Smuzhiyun 	case SNDRV_FIREWIRE_IOCTL_TASCAM_STATE:
235*4882a593Smuzhiyun 		return tscm_hwdep_state(tscm, (void __user *)arg);
236*4882a593Smuzhiyun 	default:
237*4882a593Smuzhiyun 		return -ENOIOCTLCMD;
238*4882a593Smuzhiyun 	}
239*4882a593Smuzhiyun }
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun #ifdef CONFIG_COMPAT
hwdep_compat_ioctl(struct snd_hwdep * hwdep,struct file * file,unsigned int cmd,unsigned long arg)242*4882a593Smuzhiyun static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
243*4882a593Smuzhiyun 			      unsigned int cmd, unsigned long arg)
244*4882a593Smuzhiyun {
245*4882a593Smuzhiyun 	return hwdep_ioctl(hwdep, file, cmd,
246*4882a593Smuzhiyun 			   (unsigned long)compat_ptr(arg));
247*4882a593Smuzhiyun }
248*4882a593Smuzhiyun #else
249*4882a593Smuzhiyun #define hwdep_compat_ioctl NULL
250*4882a593Smuzhiyun #endif
251*4882a593Smuzhiyun 
snd_tscm_create_hwdep_device(struct snd_tscm * tscm)252*4882a593Smuzhiyun int snd_tscm_create_hwdep_device(struct snd_tscm *tscm)
253*4882a593Smuzhiyun {
254*4882a593Smuzhiyun 	static const struct snd_hwdep_ops ops = {
255*4882a593Smuzhiyun 		.read		= hwdep_read,
256*4882a593Smuzhiyun 		.release	= hwdep_release,
257*4882a593Smuzhiyun 		.poll		= hwdep_poll,
258*4882a593Smuzhiyun 		.ioctl		= hwdep_ioctl,
259*4882a593Smuzhiyun 		.ioctl_compat	= hwdep_compat_ioctl,
260*4882a593Smuzhiyun 	};
261*4882a593Smuzhiyun 	struct snd_hwdep *hwdep;
262*4882a593Smuzhiyun 	int err;
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun 	err = snd_hwdep_new(tscm->card, "Tascam", 0, &hwdep);
265*4882a593Smuzhiyun 	if (err < 0)
266*4882a593Smuzhiyun 		return err;
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun 	strcpy(hwdep->name, "Tascam");
269*4882a593Smuzhiyun 	hwdep->iface = SNDRV_HWDEP_IFACE_FW_TASCAM;
270*4882a593Smuzhiyun 	hwdep->ops = ops;
271*4882a593Smuzhiyun 	hwdep->private_data = tscm;
272*4882a593Smuzhiyun 	hwdep->exclusive = true;
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun 	tscm->hwdep = hwdep;
275*4882a593Smuzhiyun 
276*4882a593Smuzhiyun 	return err;
277*4882a593Smuzhiyun }
278