208 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * ff-hwdep.c - a part of driver for RME Fireface series4 *5 * Copyright (c) 2015-2017 Takashi Sakamoto6 */7 8/*9 * This codes give three functionality.10 *11 * 1.get firewire node information12 * 2.get notification about starting/stopping stream13 * 3.lock/unlock stream14 */15 16#include "ff.h"17 18static bool has_msg(struct snd_ff *ff)19{20 if (ff->spec->protocol->has_msg)21 return ff->spec->protocol->has_msg(ff);22 else23 return 0;24}25 26static 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 the59 // 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 71static __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 else83 events = 0;84 spin_unlock_irq(&ff->lock);85 86 return events;87}88 89static 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 108static 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 126static 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 144static 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 156static 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_COMPAT174static 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#else181#define hwdep_compat_ioctl NULL182#endif183 184int 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