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 76 poll_wait(file, &ff->hwdep_wait, wait); 77 78 guard(spinlock_irq)(&ff->lock); 79 if (ff->dev_lock_changed || has_msg(ff)) 80 return EPOLLIN | EPOLLRDNORM; 81 else 82 return 0; 83 } 84 85 static int hwdep_get_info(struct snd_ff *ff, void __user *arg) 86 { 87 struct fw_device *dev = fw_parent_device(ff->unit); 88 struct snd_firewire_get_info info; 89 90 memset(&info, 0, sizeof(info)); 91 info.type = SNDRV_FIREWIRE_TYPE_FIREFACE; 92 info.card = dev->card->index; 93 *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); 94 *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); 95 strscpy(info.device_name, dev_name(&dev->device), 96 sizeof(info.device_name)); 97 98 if (copy_to_user(arg, &info, sizeof(info))) 99 return -EFAULT; 100 101 return 0; 102 } 103 104 static int hwdep_lock(struct snd_ff *ff) 105 { 106 guard(spinlock_irq)(&ff->lock); 107 108 if (ff->dev_lock_count == 0) { 109 ff->dev_lock_count = -1; 110 return 0; 111 } else { 112 return -EBUSY; 113 } 114 } 115 116 static int hwdep_unlock(struct snd_ff *ff) 117 { 118 guard(spinlock_irq)(&ff->lock); 119 120 if (ff->dev_lock_count == -1) { 121 ff->dev_lock_count = 0; 122 return 0; 123 } else { 124 return -EBADFD; 125 } 126 } 127 128 static int hwdep_release(struct snd_hwdep *hwdep, struct file *file) 129 { 130 struct snd_ff *ff = hwdep->private_data; 131 132 guard(spinlock_irq)(&ff->lock); 133 if (ff->dev_lock_count == -1) 134 ff->dev_lock_count = 0; 135 136 return 0; 137 } 138 139 static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, 140 unsigned int cmd, unsigned long arg) 141 { 142 struct snd_ff *ff = hwdep->private_data; 143 144 switch (cmd) { 145 case SNDRV_FIREWIRE_IOCTL_GET_INFO: 146 return hwdep_get_info(ff, (void __user *)arg); 147 case SNDRV_FIREWIRE_IOCTL_LOCK: 148 return hwdep_lock(ff); 149 case SNDRV_FIREWIRE_IOCTL_UNLOCK: 150 return hwdep_unlock(ff); 151 default: 152 return -ENOIOCTLCMD; 153 } 154 } 155 156 #ifdef CONFIG_COMPAT 157 static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, 158 unsigned int cmd, unsigned long arg) 159 { 160 return hwdep_ioctl(hwdep, file, cmd, 161 (unsigned long)compat_ptr(arg)); 162 } 163 #else 164 #define hwdep_compat_ioctl NULL 165 #endif 166 167 int snd_ff_create_hwdep_devices(struct snd_ff *ff) 168 { 169 static const struct snd_hwdep_ops hwdep_ops = { 170 .read = hwdep_read, 171 .release = hwdep_release, 172 .poll = hwdep_poll, 173 .ioctl = hwdep_ioctl, 174 .ioctl_compat = hwdep_compat_ioctl, 175 }; 176 struct snd_hwdep *hwdep; 177 int err; 178 179 err = snd_hwdep_new(ff->card, ff->card->driver, 0, &hwdep); 180 if (err < 0) 181 return err; 182 183 strscpy(hwdep->name, ff->card->driver); 184 hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREFACE; 185 hwdep->ops = hwdep_ops; 186 hwdep->private_data = ff; 187 hwdep->exclusive = true; 188 189 return 0; 190 } 191