linux/sound/firewire/fireface/ff-hwdep.c
<<
>>
Prefs
   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
  18static 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
  52static __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
  70static 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
  89static 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
 107static 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
 125static 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
 137static 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
 155static 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
 165int 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