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