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