linux/sound/firewire/bebob/bebob_hwdep.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * bebob_hwdep.c - a part of driver for BeBoB based devices
   4 *
   5 * Copyright (c) 2013-2014 Takashi Sakamoto
   6 */
   7
   8/*
   9 * This codes give three functionality.
  10 *
  11 * 1.get firewire node infomation
  12 * 2.get notification about starting/stopping stream
  13 * 3.lock/unlock stream
  14 */
  15
  16#include "bebob.h"
  17
  18static long
  19hwdep_read(struct snd_hwdep *hwdep, char __user *buf,  long count,
  20           loff_t *offset)
  21{
  22        struct snd_bebob *bebob = hwdep->private_data;
  23        DEFINE_WAIT(wait);
  24        union snd_firewire_event event;
  25
  26        spin_lock_irq(&bebob->lock);
  27
  28        while (!bebob->dev_lock_changed) {
  29                prepare_to_wait(&bebob->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
  30                spin_unlock_irq(&bebob->lock);
  31                schedule();
  32                finish_wait(&bebob->hwdep_wait, &wait);
  33                if (signal_pending(current))
  34                        return -ERESTARTSYS;
  35                spin_lock_irq(&bebob->lock);
  36        }
  37
  38        memset(&event, 0, sizeof(event));
  39        count = min_t(long, count, sizeof(event.lock_status));
  40        event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
  41        event.lock_status.status = (bebob->dev_lock_count > 0);
  42        bebob->dev_lock_changed = false;
  43
  44        spin_unlock_irq(&bebob->lock);
  45
  46        if (copy_to_user(buf, &event, count))
  47                return -EFAULT;
  48
  49        return count;
  50}
  51
  52static __poll_t
  53hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait)
  54{
  55        struct snd_bebob *bebob = hwdep->private_data;
  56        __poll_t events;
  57
  58        poll_wait(file, &bebob->hwdep_wait, wait);
  59
  60        spin_lock_irq(&bebob->lock);
  61        if (bebob->dev_lock_changed)
  62                events = EPOLLIN | EPOLLRDNORM;
  63        else
  64                events = 0;
  65        spin_unlock_irq(&bebob->lock);
  66
  67        return events;
  68}
  69
  70static int
  71hwdep_get_info(struct snd_bebob *bebob, void __user *arg)
  72{
  73        struct fw_device *dev = fw_parent_device(bebob->unit);
  74        struct snd_firewire_get_info info;
  75
  76        memset(&info, 0, sizeof(info));
  77        info.type = SNDRV_FIREWIRE_TYPE_BEBOB;
  78        info.card = dev->card->index;
  79        *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
  80        *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
  81        strscpy(info.device_name, dev_name(&dev->device),
  82                sizeof(info.device_name));
  83
  84        if (copy_to_user(arg, &info, sizeof(info)))
  85                return -EFAULT;
  86
  87        return 0;
  88}
  89
  90static int
  91hwdep_lock(struct snd_bebob *bebob)
  92{
  93        int err;
  94
  95        spin_lock_irq(&bebob->lock);
  96
  97        if (bebob->dev_lock_count == 0) {
  98                bebob->dev_lock_count = -1;
  99                err = 0;
 100        } else {
 101                err = -EBUSY;
 102        }
 103
 104        spin_unlock_irq(&bebob->lock);
 105
 106        return err;
 107}
 108
 109static int
 110hwdep_unlock(struct snd_bebob *bebob)
 111{
 112        int err;
 113
 114        spin_lock_irq(&bebob->lock);
 115
 116        if (bebob->dev_lock_count == -1) {
 117                bebob->dev_lock_count = 0;
 118                err = 0;
 119        } else {
 120                err = -EBADFD;
 121        }
 122
 123        spin_unlock_irq(&bebob->lock);
 124
 125        return err;
 126}
 127
 128static int
 129hwdep_release(struct snd_hwdep *hwdep, struct file *file)
 130{
 131        struct snd_bebob *bebob = hwdep->private_data;
 132
 133        spin_lock_irq(&bebob->lock);
 134        if (bebob->dev_lock_count == -1)
 135                bebob->dev_lock_count = 0;
 136        spin_unlock_irq(&bebob->lock);
 137
 138        return 0;
 139}
 140
 141static int
 142hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
 143            unsigned int cmd, unsigned long arg)
 144{
 145        struct snd_bebob *bebob = hwdep->private_data;
 146
 147        switch (cmd) {
 148        case SNDRV_FIREWIRE_IOCTL_GET_INFO:
 149                return hwdep_get_info(bebob, (void __user *)arg);
 150        case SNDRV_FIREWIRE_IOCTL_LOCK:
 151                return hwdep_lock(bebob);
 152        case SNDRV_FIREWIRE_IOCTL_UNLOCK:
 153                return hwdep_unlock(bebob);
 154        default:
 155                return -ENOIOCTLCMD;
 156        }
 157}
 158
 159#ifdef CONFIG_COMPAT
 160static int
 161hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
 162                   unsigned int cmd, unsigned long arg)
 163{
 164        return hwdep_ioctl(hwdep, file, cmd,
 165                           (unsigned long)compat_ptr(arg));
 166}
 167#else
 168#define hwdep_compat_ioctl NULL
 169#endif
 170
 171int snd_bebob_create_hwdep_device(struct snd_bebob *bebob)
 172{
 173        static const struct snd_hwdep_ops ops = {
 174                .read           = hwdep_read,
 175                .release        = hwdep_release,
 176                .poll           = hwdep_poll,
 177                .ioctl          = hwdep_ioctl,
 178                .ioctl_compat   = hwdep_compat_ioctl,
 179        };
 180        struct snd_hwdep *hwdep;
 181        int err;
 182
 183        err = snd_hwdep_new(bebob->card, "BeBoB", 0, &hwdep);
 184        if (err < 0)
 185                goto end;
 186        strcpy(hwdep->name, "BeBoB");
 187        hwdep->iface = SNDRV_HWDEP_IFACE_FW_BEBOB;
 188        hwdep->ops = ops;
 189        hwdep->private_data = bebob;
 190        hwdep->exclusive = true;
 191end:
 192        return err;
 193}
 194
 195