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