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
19static long
20hwdep_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
56static unsigned int
57hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait)
58{
59	struct snd_bebob *bebob = hwdep->private_data;
60	unsigned int 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 = POLLIN | POLLRDNORM;
67	else
68		events = 0;
69	spin_unlock_irq(&bebob->lock);
70
71	return events;
72}
73
74static int
75hwdep_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
94static int
95hwdep_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
113static int
114hwdep_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
132static int
133hwdep_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
145static int
146hwdep_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
164static int
165hwdep_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
175static const struct snd_hwdep_ops hwdep_ops = {
176	.read		= hwdep_read,
177	.release	= hwdep_release,
178	.poll		= hwdep_poll,
179	.ioctl		= hwdep_ioctl,
180	.ioctl_compat	= hwdep_compat_ioctl,
181};
182
183int snd_bebob_create_hwdep_device(struct snd_bebob *bebob)
184{
185	struct snd_hwdep *hwdep;
186	int err;
187
188	err = snd_hwdep_new(bebob->card, "BeBoB", 0, &hwdep);
189	if (err < 0)
190		goto end;
191	strcpy(hwdep->name, "BeBoB");
192	hwdep->iface = SNDRV_HWDEP_IFACE_FW_BEBOB;
193	hwdep->ops = hwdep_ops;
194	hwdep->private_data = bebob;
195	hwdep->exclusive = true;
196end:
197	return err;
198}
199
200