Merge tag 'docs-5.11' of git://git.lwn.net/linux
[linux-2.6-microblaze.git] / sound / firewire / fireworks / fireworks_hwdep.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * fireworks_hwdep.c - a part of driver for Fireworks based devices
4  *
5  * Copyright (c) 2013-2014 Takashi Sakamoto
6  */
7
8 /*
9  * This codes have five functionalities.
10  *
11  * 1.get information about firewire node
12  * 2.get notification about starting/stopping stream
13  * 3.lock/unlock streaming
14  * 4.transmit command of EFW transaction
15  * 5.receive response of EFW transaction
16  *
17  */
18
19 #include "fireworks.h"
20
21 static long
22 hwdep_read_resp_buf(struct snd_efw *efw, char __user *buf, long remained,
23                     loff_t *offset)
24 {
25         unsigned int length, till_end, type;
26         struct snd_efw_transaction *t;
27         u8 *pull_ptr;
28         long count = 0;
29
30         if (remained < sizeof(type) + sizeof(struct snd_efw_transaction))
31                 return -ENOSPC;
32
33         /* data type is SNDRV_FIREWIRE_EVENT_EFW_RESPONSE */
34         type = SNDRV_FIREWIRE_EVENT_EFW_RESPONSE;
35         if (copy_to_user(buf, &type, sizeof(type)))
36                 return -EFAULT;
37         remained -= sizeof(type);
38         buf += sizeof(type);
39
40         /* write into buffer as many responses as possible */
41         spin_lock_irq(&efw->lock);
42
43         /*
44          * When another task reaches here during this task's access to user
45          * space, it picks up current position in buffer and can read the same
46          * series of responses.
47          */
48         pull_ptr = efw->pull_ptr;
49
50         while (efw->push_ptr != pull_ptr) {
51                 t = (struct snd_efw_transaction *)(pull_ptr);
52                 length = be32_to_cpu(t->length) * sizeof(__be32);
53
54                 /* confirm enough space for this response */
55                 if (remained < length)
56                         break;
57
58                 /* copy from ring buffer to user buffer */
59                 while (length > 0) {
60                         till_end = snd_efw_resp_buf_size -
61                                 (unsigned int)(pull_ptr - efw->resp_buf);
62                         till_end = min_t(unsigned int, length, till_end);
63
64                         spin_unlock_irq(&efw->lock);
65
66                         if (copy_to_user(buf, pull_ptr, till_end))
67                                 return -EFAULT;
68
69                         spin_lock_irq(&efw->lock);
70
71                         pull_ptr += till_end;
72                         if (pull_ptr >= efw->resp_buf + snd_efw_resp_buf_size)
73                                 pull_ptr -= snd_efw_resp_buf_size;
74
75                         length -= till_end;
76                         buf += till_end;
77                         count += till_end;
78                         remained -= till_end;
79                 }
80         }
81
82         /*
83          * All of tasks can read from the buffer nearly simultaneously, but the
84          * last position for each task is different depending on the length of
85          * given buffer. Here, for simplicity, a position of buffer is set by
86          * the latest task. It's better for a listening application to allow one
87          * thread to read from the buffer. Unless, each task can read different
88          * sequence of responses depending on variation of buffer length.
89          */
90         efw->pull_ptr = pull_ptr;
91
92         spin_unlock_irq(&efw->lock);
93
94         return count;
95 }
96
97 static long
98 hwdep_read_locked(struct snd_efw *efw, char __user *buf, long count,
99                   loff_t *offset)
100 {
101         union snd_firewire_event event = {
102                 .lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,
103         };
104
105         spin_lock_irq(&efw->lock);
106
107         event.lock_status.status = (efw->dev_lock_count > 0);
108         efw->dev_lock_changed = false;
109
110         spin_unlock_irq(&efw->lock);
111
112         count = min_t(long, count, sizeof(event.lock_status));
113
114         if (copy_to_user(buf, &event, count))
115                 return -EFAULT;
116
117         return count;
118 }
119
120 static long
121 hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
122            loff_t *offset)
123 {
124         struct snd_efw *efw = hwdep->private_data;
125         DEFINE_WAIT(wait);
126         bool dev_lock_changed;
127         bool queued;
128
129         spin_lock_irq(&efw->lock);
130
131         dev_lock_changed = efw->dev_lock_changed;
132         queued = efw->push_ptr != efw->pull_ptr;
133
134         while (!dev_lock_changed && !queued) {
135                 prepare_to_wait(&efw->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
136                 spin_unlock_irq(&efw->lock);
137                 schedule();
138                 finish_wait(&efw->hwdep_wait, &wait);
139                 if (signal_pending(current))
140                         return -ERESTARTSYS;
141                 spin_lock_irq(&efw->lock);
142                 dev_lock_changed = efw->dev_lock_changed;
143                 queued = efw->push_ptr != efw->pull_ptr;
144         }
145
146         spin_unlock_irq(&efw->lock);
147
148         if (dev_lock_changed)
149                 count = hwdep_read_locked(efw, buf, count, offset);
150         else if (queued)
151                 count = hwdep_read_resp_buf(efw, buf, count, offset);
152
153         return count;
154 }
155
156 static long
157 hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count,
158             loff_t *offset)
159 {
160         struct snd_efw *efw = hwdep->private_data;
161         u32 seqnum;
162         u8 *buf;
163
164         if (count < sizeof(struct snd_efw_transaction) ||
165             SND_EFW_RESPONSE_MAXIMUM_BYTES < count)
166                 return -EINVAL;
167
168         buf = memdup_user(data, count);
169         if (IS_ERR(buf))
170                 return PTR_ERR(buf);
171
172         /* check seqnum is not for kernel-land */
173         seqnum = be32_to_cpu(((struct snd_efw_transaction *)buf)->seqnum);
174         if (seqnum > SND_EFW_TRANSACTION_USER_SEQNUM_MAX) {
175                 count = -EINVAL;
176                 goto end;
177         }
178
179         if (snd_efw_transaction_cmd(efw->unit, buf, count) < 0)
180                 count = -EIO;
181 end:
182         kfree(buf);
183         return count;
184 }
185
186 static __poll_t
187 hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait)
188 {
189         struct snd_efw *efw = hwdep->private_data;
190         __poll_t events;
191
192         poll_wait(file, &efw->hwdep_wait, wait);
193
194         spin_lock_irq(&efw->lock);
195         if (efw->dev_lock_changed || efw->pull_ptr != efw->push_ptr)
196                 events = EPOLLIN | EPOLLRDNORM;
197         else
198                 events = 0;
199         spin_unlock_irq(&efw->lock);
200
201         return events | EPOLLOUT;
202 }
203
204 static int
205 hwdep_get_info(struct snd_efw *efw, void __user *arg)
206 {
207         struct fw_device *dev = fw_parent_device(efw->unit);
208         struct snd_firewire_get_info info;
209
210         memset(&info, 0, sizeof(info));
211         info.type = SNDRV_FIREWIRE_TYPE_FIREWORKS;
212         info.card = dev->card->index;
213         *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
214         *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
215         strlcpy(info.device_name, dev_name(&dev->device),
216                 sizeof(info.device_name));
217
218         if (copy_to_user(arg, &info, sizeof(info)))
219                 return -EFAULT;
220
221         return 0;
222 }
223
224 static int
225 hwdep_lock(struct snd_efw *efw)
226 {
227         int err;
228
229         spin_lock_irq(&efw->lock);
230
231         if (efw->dev_lock_count == 0) {
232                 efw->dev_lock_count = -1;
233                 err = 0;
234         } else {
235                 err = -EBUSY;
236         }
237
238         spin_unlock_irq(&efw->lock);
239
240         return err;
241 }
242
243 static int
244 hwdep_unlock(struct snd_efw *efw)
245 {
246         int err;
247
248         spin_lock_irq(&efw->lock);
249
250         if (efw->dev_lock_count == -1) {
251                 efw->dev_lock_count = 0;
252                 err = 0;
253         } else {
254                 err = -EBADFD;
255         }
256
257         spin_unlock_irq(&efw->lock);
258
259         return err;
260 }
261
262 static int
263 hwdep_release(struct snd_hwdep *hwdep, struct file *file)
264 {
265         struct snd_efw *efw = hwdep->private_data;
266
267         spin_lock_irq(&efw->lock);
268         if (efw->dev_lock_count == -1)
269                 efw->dev_lock_count = 0;
270         spin_unlock_irq(&efw->lock);
271
272         return 0;
273 }
274
275 static int
276 hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
277             unsigned int cmd, unsigned long arg)
278 {
279         struct snd_efw *efw = hwdep->private_data;
280
281         switch (cmd) {
282         case SNDRV_FIREWIRE_IOCTL_GET_INFO:
283                 return hwdep_get_info(efw, (void __user *)arg);
284         case SNDRV_FIREWIRE_IOCTL_LOCK:
285                 return hwdep_lock(efw);
286         case SNDRV_FIREWIRE_IOCTL_UNLOCK:
287                 return hwdep_unlock(efw);
288         default:
289                 return -ENOIOCTLCMD;
290         }
291 }
292
293 #ifdef CONFIG_COMPAT
294 static int
295 hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
296                    unsigned int cmd, unsigned long arg)
297 {
298         return hwdep_ioctl(hwdep, file, cmd,
299                            (unsigned long)compat_ptr(arg));
300 }
301 #else
302 #define hwdep_compat_ioctl NULL
303 #endif
304
305 int snd_efw_create_hwdep_device(struct snd_efw *efw)
306 {
307         static const struct snd_hwdep_ops ops = {
308                 .read           = hwdep_read,
309                 .write          = hwdep_write,
310                 .release        = hwdep_release,
311                 .poll           = hwdep_poll,
312                 .ioctl          = hwdep_ioctl,
313                 .ioctl_compat   = hwdep_compat_ioctl,
314         };
315         struct snd_hwdep *hwdep;
316         int err;
317
318         err = snd_hwdep_new(efw->card, "Fireworks", 0, &hwdep);
319         if (err < 0)
320                 goto end;
321         strcpy(hwdep->name, "Fireworks");
322         hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREWORKS;
323         hwdep->ops = ops;
324         hwdep->private_data = efw;
325         hwdep->exclusive = true;
326 end:
327         return err;
328 }
329