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

TOMOYO Linux Cross Reference
Linux/sound/firewire/fireface/ff-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  * 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 bool has_msg(struct snd_ff *ff)
 19 {
 20         if (ff->spec->protocol->has_msg)
 21                 return ff->spec->protocol->has_msg(ff);
 22         else
 23                 return 0;
 24 }
 25 
 26 static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf,  long count,
 27                        loff_t *offset)
 28 {
 29         struct snd_ff *ff = hwdep->private_data;
 30         DEFINE_WAIT(wait);
 31 
 32         spin_lock_irq(&ff->lock);
 33 
 34         while (!ff->dev_lock_changed && !has_msg(ff)) {
 35                 prepare_to_wait(&ff->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
 36                 spin_unlock_irq(&ff->lock);
 37                 schedule();
 38                 finish_wait(&ff->hwdep_wait, &wait);
 39                 if (signal_pending(current))
 40                         return -ERESTARTSYS;
 41                 spin_lock_irq(&ff->lock);
 42         }
 43 
 44         if (ff->dev_lock_changed && count >= sizeof(struct snd_firewire_event_lock_status)) {
 45                 struct snd_firewire_event_lock_status ev = {
 46                         .type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,
 47                         .status = (ff->dev_lock_count > 0),
 48                 };
 49 
 50                 ff->dev_lock_changed = false;
 51 
 52                 spin_unlock_irq(&ff->lock);
 53 
 54                 if (copy_to_user(buf, &ev, sizeof(ev)))
 55                         return -EFAULT;
 56                 count = sizeof(ev);
 57         } else if (has_msg(ff)) {
 58                 // NOTE: Acquired spin lock should be released before accessing to user space in the
 59                 // callback since the access can cause page fault.
 60                 count = ff->spec->protocol->copy_msg_to_user(ff, buf, count);
 61                 spin_unlock_irq(&ff->lock);
 62         } else {
 63                 spin_unlock_irq(&ff->lock);
 64 
 65                 count = 0;
 66         }
 67 
 68         return count;
 69 }
 70 
 71 static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
 72                                poll_table *wait)
 73 {
 74         struct snd_ff *ff = hwdep->private_data;
 75         __poll_t events;
 76 
 77         poll_wait(file, &ff->hwdep_wait, wait);
 78 
 79         spin_lock_irq(&ff->lock);
 80         if (ff->dev_lock_changed || has_msg(ff))
 81                 events = EPOLLIN | EPOLLRDNORM;
 82         else
 83                 events = 0;
 84         spin_unlock_irq(&ff->lock);
 85 
 86         return events;
 87 }
 88 
 89 static int hwdep_get_info(struct snd_ff *ff, void __user *arg)
 90 {
 91         struct fw_device *dev = fw_parent_device(ff->unit);
 92         struct snd_firewire_get_info info;
 93 
 94         memset(&info, 0, sizeof(info));
 95         info.type = SNDRV_FIREWIRE_TYPE_FIREFACE;
 96         info.card = dev->card->index;
 97         *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
 98         *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
 99         strscpy(info.device_name, dev_name(&dev->device),
100                 sizeof(info.device_name));
101 
102         if (copy_to_user(arg, &info, sizeof(info)))
103                 return -EFAULT;
104 
105         return 0;
106 }
107 
108 static int hwdep_lock(struct snd_ff *ff)
109 {
110         int err;
111 
112         spin_lock_irq(&ff->lock);
113 
114         if (ff->dev_lock_count == 0) {
115                 ff->dev_lock_count = -1;
116                 err = 0;
117         } else {
118                 err = -EBUSY;
119         }
120 
121         spin_unlock_irq(&ff->lock);
122 
123         return err;
124 }
125 
126 static int hwdep_unlock(struct snd_ff *ff)
127 {
128         int err;
129 
130         spin_lock_irq(&ff->lock);
131 
132         if (ff->dev_lock_count == -1) {
133                 ff->dev_lock_count = 0;
134                 err = 0;
135         } else {
136                 err = -EBADFD;
137         }
138 
139         spin_unlock_irq(&ff->lock);
140 
141         return err;
142 }
143 
144 static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
145 {
146         struct snd_ff *ff = hwdep->private_data;
147 
148         spin_lock_irq(&ff->lock);
149         if (ff->dev_lock_count == -1)
150                 ff->dev_lock_count = 0;
151         spin_unlock_irq(&ff->lock);
152 
153         return 0;
154 }
155 
156 static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
157                        unsigned int cmd, unsigned long arg)
158 {
159         struct snd_ff *ff = hwdep->private_data;
160 
161         switch (cmd) {
162         case SNDRV_FIREWIRE_IOCTL_GET_INFO:
163                 return hwdep_get_info(ff, (void __user *)arg);
164         case SNDRV_FIREWIRE_IOCTL_LOCK:
165                 return hwdep_lock(ff);
166         case SNDRV_FIREWIRE_IOCTL_UNLOCK:
167                 return hwdep_unlock(ff);
168         default:
169                 return -ENOIOCTLCMD;
170         }
171 }
172 
173 #ifdef CONFIG_COMPAT
174 static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
175                               unsigned int cmd, unsigned long arg)
176 {
177         return hwdep_ioctl(hwdep, file, cmd,
178                            (unsigned long)compat_ptr(arg));
179 }
180 #else
181 #define hwdep_compat_ioctl NULL
182 #endif
183 
184 int snd_ff_create_hwdep_devices(struct snd_ff *ff)
185 {
186         static const struct snd_hwdep_ops hwdep_ops = {
187                 .read           = hwdep_read,
188                 .release        = hwdep_release,
189                 .poll           = hwdep_poll,
190                 .ioctl          = hwdep_ioctl,
191                 .ioctl_compat   = hwdep_compat_ioctl,
192         };
193         struct snd_hwdep *hwdep;
194         int err;
195 
196         err = snd_hwdep_new(ff->card, ff->card->driver, 0, &hwdep);
197         if (err < 0)
198                 return err;
199 
200         strcpy(hwdep->name, ff->card->driver);
201         hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREFACE;
202         hwdep->ops = hwdep_ops;
203         hwdep->private_data = ff;
204         hwdep->exclusive = true;
205 
206         return 0;
207 }
208 

~ [ 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