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