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