linux/sound/firewire/oxfw/oxfw-hwdep.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * oxfw_hwdep.c - a part of driver for OXFW970/971 based devices
   4 *
   5 * Copyright (c) 2014 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 "oxfw.h"
  17
  18static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf,  long count,
  19                       loff_t *offset)
  20{
  21        struct snd_oxfw *oxfw = hwdep->private_data;
  22        DEFINE_WAIT(wait);
  23        union snd_firewire_event event;
  24
  25        spin_lock_irq(&oxfw->lock);
  26
  27        while (!oxfw->dev_lock_changed) {
  28                prepare_to_wait(&oxfw->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
  29                spin_unlock_irq(&oxfw->lock);
  30                schedule();
  31                finish_wait(&oxfw->hwdep_wait, &wait);
  32                if (signal_pending(current))
  33                        return -ERESTARTSYS;
  34                spin_lock_irq(&oxfw->lock);
  35        }
  36
  37        memset(&event, 0, sizeof(event));
  38        event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
  39        event.lock_status.status = (oxfw->dev_lock_count > 0);
  40        oxfw->dev_lock_changed = false;
  41
  42        count = min_t(long, count, sizeof(event.lock_status));
  43
  44        spin_unlock_irq(&oxfw->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_oxfw *oxfw = hwdep->private_data;
  56        __poll_t events;
  57
  58        poll_wait(file, &oxfw->hwdep_wait, wait);
  59
  60        spin_lock_irq(&oxfw->lock);
  61        if (oxfw->dev_lock_changed)
  62                events = EPOLLIN | EPOLLRDNORM;
  63        else
  64                events = 0;
  65        spin_unlock_irq(&oxfw->lock);
  66
  67        return events;
  68}
  69
  70static int hwdep_get_info(struct snd_oxfw *oxfw, void __user *arg)
  71{
  72        struct fw_device *dev = fw_parent_device(oxfw->unit);
  73        struct snd_firewire_get_info info;
  74
  75        memset(&info, 0, sizeof(info));
  76        info.type = SNDRV_FIREWIRE_TYPE_OXFW;
  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_oxfw *oxfw)
  90{
  91        int err;
  92
  93        spin_lock_irq(&oxfw->lock);
  94
  95        if (oxfw->dev_lock_count == 0) {
  96                oxfw->dev_lock_count = -1;
  97                err = 0;
  98        } else {
  99                err = -EBUSY;
 100        }
 101
 102        spin_unlock_irq(&oxfw->lock);
 103
 104        return err;
 105}
 106
 107static int hwdep_unlock(struct snd_oxfw *oxfw)
 108{
 109        int err;
 110
 111        spin_lock_irq(&oxfw->lock);
 112
 113        if (oxfw->dev_lock_count == -1) {
 114                oxfw->dev_lock_count = 0;
 115                err = 0;
 116        } else {
 117                err = -EBADFD;
 118        }
 119
 120        spin_unlock_irq(&oxfw->lock);
 121
 122        return err;
 123}
 124
 125static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
 126{
 127        struct snd_oxfw *oxfw = hwdep->private_data;
 128
 129        spin_lock_irq(&oxfw->lock);
 130        if (oxfw->dev_lock_count == -1)
 131                oxfw->dev_lock_count = 0;
 132        spin_unlock_irq(&oxfw->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_oxfw *oxfw = hwdep->private_data;
 141
 142        switch (cmd) {
 143        case SNDRV_FIREWIRE_IOCTL_GET_INFO:
 144                return hwdep_get_info(oxfw, (void __user *)arg);
 145        case SNDRV_FIREWIRE_IOCTL_LOCK:
 146                return hwdep_lock(oxfw);
 147        case SNDRV_FIREWIRE_IOCTL_UNLOCK:
 148                return hwdep_unlock(oxfw);
 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_oxfw_create_hwdep(struct snd_oxfw *oxfw)
 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(oxfw->card, oxfw->card->driver, 0, &hwdep);
 178        if (err < 0)
 179                goto end;
 180        strcpy(hwdep->name, oxfw->card->driver);
 181        hwdep->iface = SNDRV_HWDEP_IFACE_FW_OXFW;
 182        hwdep->ops = hwdep_ops;
 183        hwdep->private_data = oxfw;
 184        hwdep->exclusive = true;
 185end:
 186        return err;
 187}
 188