xref: /OK3568_Linux_fs/kernel/sound/firewire/bebob/bebob_hwdep.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * bebob_hwdep.c - a part of driver for BeBoB based devices
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (c) 2013-2014 Takashi Sakamoto
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun /*
9*4882a593Smuzhiyun  * This codes give three functionality.
10*4882a593Smuzhiyun  *
11*4882a593Smuzhiyun  * 1.get firewire node infomation
12*4882a593Smuzhiyun  * 2.get notification about starting/stopping stream
13*4882a593Smuzhiyun  * 3.lock/unlock stream
14*4882a593Smuzhiyun  */
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun #include "bebob.h"
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun static long
hwdep_read(struct snd_hwdep * hwdep,char __user * buf,long count,loff_t * offset)19*4882a593Smuzhiyun hwdep_read(struct snd_hwdep *hwdep, char __user *buf,  long count,
20*4882a593Smuzhiyun 	   loff_t *offset)
21*4882a593Smuzhiyun {
22*4882a593Smuzhiyun 	struct snd_bebob *bebob = hwdep->private_data;
23*4882a593Smuzhiyun 	DEFINE_WAIT(wait);
24*4882a593Smuzhiyun 	union snd_firewire_event event;
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun 	spin_lock_irq(&bebob->lock);
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun 	while (!bebob->dev_lock_changed) {
29*4882a593Smuzhiyun 		prepare_to_wait(&bebob->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
30*4882a593Smuzhiyun 		spin_unlock_irq(&bebob->lock);
31*4882a593Smuzhiyun 		schedule();
32*4882a593Smuzhiyun 		finish_wait(&bebob->hwdep_wait, &wait);
33*4882a593Smuzhiyun 		if (signal_pending(current))
34*4882a593Smuzhiyun 			return -ERESTARTSYS;
35*4882a593Smuzhiyun 		spin_lock_irq(&bebob->lock);
36*4882a593Smuzhiyun 	}
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun 	memset(&event, 0, sizeof(event));
39*4882a593Smuzhiyun 	count = min_t(long, count, sizeof(event.lock_status));
40*4882a593Smuzhiyun 	if (bebob->dev_lock_changed) {
41*4882a593Smuzhiyun 		event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
42*4882a593Smuzhiyun 		event.lock_status.status = (bebob->dev_lock_count > 0);
43*4882a593Smuzhiyun 		bebob->dev_lock_changed = false;
44*4882a593Smuzhiyun 	}
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun 	spin_unlock_irq(&bebob->lock);
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun 	if (copy_to_user(buf, &event, count))
49*4882a593Smuzhiyun 		return -EFAULT;
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun 	return count;
52*4882a593Smuzhiyun }
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun static __poll_t
hwdep_poll(struct snd_hwdep * hwdep,struct file * file,poll_table * wait)55*4882a593Smuzhiyun hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait)
56*4882a593Smuzhiyun {
57*4882a593Smuzhiyun 	struct snd_bebob *bebob = hwdep->private_data;
58*4882a593Smuzhiyun 	__poll_t events;
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 	poll_wait(file, &bebob->hwdep_wait, wait);
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun 	spin_lock_irq(&bebob->lock);
63*4882a593Smuzhiyun 	if (bebob->dev_lock_changed)
64*4882a593Smuzhiyun 		events = EPOLLIN | EPOLLRDNORM;
65*4882a593Smuzhiyun 	else
66*4882a593Smuzhiyun 		events = 0;
67*4882a593Smuzhiyun 	spin_unlock_irq(&bebob->lock);
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun 	return events;
70*4882a593Smuzhiyun }
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun static int
hwdep_get_info(struct snd_bebob * bebob,void __user * arg)73*4882a593Smuzhiyun hwdep_get_info(struct snd_bebob *bebob, void __user *arg)
74*4882a593Smuzhiyun {
75*4882a593Smuzhiyun 	struct fw_device *dev = fw_parent_device(bebob->unit);
76*4882a593Smuzhiyun 	struct snd_firewire_get_info info;
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 	memset(&info, 0, sizeof(info));
79*4882a593Smuzhiyun 	info.type = SNDRV_FIREWIRE_TYPE_BEBOB;
80*4882a593Smuzhiyun 	info.card = dev->card->index;
81*4882a593Smuzhiyun 	*(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
82*4882a593Smuzhiyun 	*(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
83*4882a593Smuzhiyun 	strlcpy(info.device_name, dev_name(&dev->device),
84*4882a593Smuzhiyun 		sizeof(info.device_name));
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun 	if (copy_to_user(arg, &info, sizeof(info)))
87*4882a593Smuzhiyun 		return -EFAULT;
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	return 0;
90*4882a593Smuzhiyun }
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun static int
hwdep_lock(struct snd_bebob * bebob)93*4882a593Smuzhiyun hwdep_lock(struct snd_bebob *bebob)
94*4882a593Smuzhiyun {
95*4882a593Smuzhiyun 	int err;
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 	spin_lock_irq(&bebob->lock);
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	if (bebob->dev_lock_count == 0) {
100*4882a593Smuzhiyun 		bebob->dev_lock_count = -1;
101*4882a593Smuzhiyun 		err = 0;
102*4882a593Smuzhiyun 	} else {
103*4882a593Smuzhiyun 		err = -EBUSY;
104*4882a593Smuzhiyun 	}
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	spin_unlock_irq(&bebob->lock);
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 	return err;
109*4882a593Smuzhiyun }
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun static int
hwdep_unlock(struct snd_bebob * bebob)112*4882a593Smuzhiyun hwdep_unlock(struct snd_bebob *bebob)
113*4882a593Smuzhiyun {
114*4882a593Smuzhiyun 	int err;
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 	spin_lock_irq(&bebob->lock);
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	if (bebob->dev_lock_count == -1) {
119*4882a593Smuzhiyun 		bebob->dev_lock_count = 0;
120*4882a593Smuzhiyun 		err = 0;
121*4882a593Smuzhiyun 	} else {
122*4882a593Smuzhiyun 		err = -EBADFD;
123*4882a593Smuzhiyun 	}
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun 	spin_unlock_irq(&bebob->lock);
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun 	return err;
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun static int
hwdep_release(struct snd_hwdep * hwdep,struct file * file)131*4882a593Smuzhiyun hwdep_release(struct snd_hwdep *hwdep, struct file *file)
132*4882a593Smuzhiyun {
133*4882a593Smuzhiyun 	struct snd_bebob *bebob = hwdep->private_data;
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun 	spin_lock_irq(&bebob->lock);
136*4882a593Smuzhiyun 	if (bebob->dev_lock_count == -1)
137*4882a593Smuzhiyun 		bebob->dev_lock_count = 0;
138*4882a593Smuzhiyun 	spin_unlock_irq(&bebob->lock);
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 	return 0;
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun static int
hwdep_ioctl(struct snd_hwdep * hwdep,struct file * file,unsigned int cmd,unsigned long arg)144*4882a593Smuzhiyun hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
145*4882a593Smuzhiyun 	    unsigned int cmd, unsigned long arg)
146*4882a593Smuzhiyun {
147*4882a593Smuzhiyun 	struct snd_bebob *bebob = hwdep->private_data;
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun 	switch (cmd) {
150*4882a593Smuzhiyun 	case SNDRV_FIREWIRE_IOCTL_GET_INFO:
151*4882a593Smuzhiyun 		return hwdep_get_info(bebob, (void __user *)arg);
152*4882a593Smuzhiyun 	case SNDRV_FIREWIRE_IOCTL_LOCK:
153*4882a593Smuzhiyun 		return hwdep_lock(bebob);
154*4882a593Smuzhiyun 	case SNDRV_FIREWIRE_IOCTL_UNLOCK:
155*4882a593Smuzhiyun 		return hwdep_unlock(bebob);
156*4882a593Smuzhiyun 	default:
157*4882a593Smuzhiyun 		return -ENOIOCTLCMD;
158*4882a593Smuzhiyun 	}
159*4882a593Smuzhiyun }
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun #ifdef CONFIG_COMPAT
162*4882a593Smuzhiyun static int
hwdep_compat_ioctl(struct snd_hwdep * hwdep,struct file * file,unsigned int cmd,unsigned long arg)163*4882a593Smuzhiyun hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
164*4882a593Smuzhiyun 		   unsigned int cmd, unsigned long arg)
165*4882a593Smuzhiyun {
166*4882a593Smuzhiyun 	return hwdep_ioctl(hwdep, file, cmd,
167*4882a593Smuzhiyun 			   (unsigned long)compat_ptr(arg));
168*4882a593Smuzhiyun }
169*4882a593Smuzhiyun #else
170*4882a593Smuzhiyun #define hwdep_compat_ioctl NULL
171*4882a593Smuzhiyun #endif
172*4882a593Smuzhiyun 
snd_bebob_create_hwdep_device(struct snd_bebob * bebob)173*4882a593Smuzhiyun int snd_bebob_create_hwdep_device(struct snd_bebob *bebob)
174*4882a593Smuzhiyun {
175*4882a593Smuzhiyun 	static const struct snd_hwdep_ops ops = {
176*4882a593Smuzhiyun 		.read		= hwdep_read,
177*4882a593Smuzhiyun 		.release	= hwdep_release,
178*4882a593Smuzhiyun 		.poll		= hwdep_poll,
179*4882a593Smuzhiyun 		.ioctl		= hwdep_ioctl,
180*4882a593Smuzhiyun 		.ioctl_compat	= hwdep_compat_ioctl,
181*4882a593Smuzhiyun 	};
182*4882a593Smuzhiyun 	struct snd_hwdep *hwdep;
183*4882a593Smuzhiyun 	int err;
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 	err = snd_hwdep_new(bebob->card, "BeBoB", 0, &hwdep);
186*4882a593Smuzhiyun 	if (err < 0)
187*4882a593Smuzhiyun 		goto end;
188*4882a593Smuzhiyun 	strcpy(hwdep->name, "BeBoB");
189*4882a593Smuzhiyun 	hwdep->iface = SNDRV_HWDEP_IFACE_FW_BEBOB;
190*4882a593Smuzhiyun 	hwdep->ops = ops;
191*4882a593Smuzhiyun 	hwdep->private_data = bebob;
192*4882a593Smuzhiyun 	hwdep->exclusive = true;
193*4882a593Smuzhiyun end:
194*4882a593Smuzhiyun 	return err;
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun 
197