linux/sound/firewire/tascam/tascam-hwdep.c
<<
>>
Prefs
   1/*
   2 * tascam-hwdep.c - a part of driver for TASCAM FireWire series
   3 *
   4 * Copyright (c) 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 */
  16
  17#include "tascam.h"
  18
  19static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
  20                       loff_t *offset)
  21{
  22        struct snd_tscm *tscm = hwdep->private_data;
  23        DEFINE_WAIT(wait);
  24        union snd_firewire_event event = {
  25                .lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,
  26        };
  27
  28        spin_lock_irq(&tscm->lock);
  29
  30        while (!tscm->dev_lock_changed) {
  31                prepare_to_wait(&tscm->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
  32                spin_unlock_irq(&tscm->lock);
  33                schedule();
  34                finish_wait(&tscm->hwdep_wait, &wait);
  35                if (signal_pending(current))
  36                        return -ERESTARTSYS;
  37                spin_lock_irq(&tscm->lock);
  38        }
  39
  40        event.lock_status.status = (tscm->dev_lock_count > 0);
  41        tscm->dev_lock_changed = false;
  42
  43        spin_unlock_irq(&tscm->lock);
  44
  45        count = min_t(long, count, sizeof(event.lock_status));
  46
  47        if (copy_to_user(buf, &event, count))
  48                return -EFAULT;
  49
  50        return count;
  51}
  52
  53static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
  54                               poll_table *wait)
  55{
  56        struct snd_tscm *tscm = hwdep->private_data;
  57        unsigned int events;
  58
  59        poll_wait(file, &tscm->hwdep_wait, wait);
  60
  61        spin_lock_irq(&tscm->lock);
  62        if (tscm->dev_lock_changed)
  63                events = POLLIN | POLLRDNORM;
  64        else
  65                events = 0;
  66        spin_unlock_irq(&tscm->lock);
  67
  68        return events;
  69}
  70
  71static int hwdep_get_info(struct snd_tscm *tscm, void __user *arg)
  72{
  73        struct fw_device *dev = fw_parent_device(tscm->unit);
  74        struct snd_firewire_get_info info;
  75
  76        memset(&info, 0, sizeof(info));
  77        info.type = SNDRV_FIREWIRE_TYPE_TASCAM;
  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        strlcpy(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 hwdep_lock(struct snd_tscm *tscm)
  91{
  92        int err;
  93
  94        spin_lock_irq(&tscm->lock);
  95
  96        if (tscm->dev_lock_count == 0) {
  97                tscm->dev_lock_count = -1;
  98                err = 0;
  99        } else {
 100                err = -EBUSY;
 101        }
 102
 103        spin_unlock_irq(&tscm->lock);
 104
 105        return err;
 106}
 107
 108static int hwdep_unlock(struct snd_tscm *tscm)
 109{
 110        int err;
 111
 112        spin_lock_irq(&tscm->lock);
 113
 114        if (tscm->dev_lock_count == -1) {
 115                tscm->dev_lock_count = 0;
 116                err = 0;
 117        } else {
 118                err = -EBADFD;
 119        }
 120
 121        spin_unlock_irq(&tscm->lock);
 122
 123        return err;
 124}
 125
 126static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
 127{
 128        struct snd_tscm *tscm = hwdep->private_data;
 129
 130        spin_lock_irq(&tscm->lock);
 131        if (tscm->dev_lock_count == -1)
 132                tscm->dev_lock_count = 0;
 133        spin_unlock_irq(&tscm->lock);
 134
 135        return 0;
 136}
 137
 138static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
 139            unsigned int cmd, unsigned long arg)
 140{
 141        struct snd_tscm *tscm = hwdep->private_data;
 142
 143        switch (cmd) {
 144        case SNDRV_FIREWIRE_IOCTL_GET_INFO:
 145                return hwdep_get_info(tscm, (void __user *)arg);
 146        case SNDRV_FIREWIRE_IOCTL_LOCK:
 147                return hwdep_lock(tscm);
 148        case SNDRV_FIREWIRE_IOCTL_UNLOCK:
 149                return hwdep_unlock(tscm);
 150        default:
 151                return -ENOIOCTLCMD;
 152        }
 153}
 154
 155#ifdef CONFIG_COMPAT
 156static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
 157                              unsigned int cmd, unsigned long arg)
 158{
 159        return hwdep_ioctl(hwdep, file, cmd,
 160                           (unsigned long)compat_ptr(arg));
 161}
 162#else
 163#define hwdep_compat_ioctl NULL
 164#endif
 165
 166int snd_tscm_create_hwdep_device(struct snd_tscm *tscm)
 167{
 168        static const struct snd_hwdep_ops ops = {
 169                .read           = hwdep_read,
 170                .release        = hwdep_release,
 171                .poll           = hwdep_poll,
 172                .ioctl          = hwdep_ioctl,
 173                .ioctl_compat   = hwdep_compat_ioctl,
 174        };
 175        struct snd_hwdep *hwdep;
 176        int err;
 177
 178        err = snd_hwdep_new(tscm->card, "Tascam", 0, &hwdep);
 179        if (err < 0)
 180                return err;
 181
 182        strcpy(hwdep->name, "Tascam");
 183        hwdep->iface = SNDRV_HWDEP_IFACE_FW_TASCAM;
 184        hwdep->ops = ops;
 185        hwdep->private_data = tscm;
 186        hwdep->exclusive = true;
 187
 188        return err;
 189}
 190