~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/firewire/fireworks/fireworks_hwdep.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php