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