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