linux/sound/firewire/fireworks/fireworks_hwdep.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * fireworks_hwdep.c - a part of driver for Fireworks based devices
   4 *
   5 * Copyright (c) 2013-2014 Takashi Sakamoto
   6 */
   7
   8/*
   9 * This codes have five functionalities.
  10 *
  11 * 1.get information about firewire node
  12 * 2.get notification about starting/stopping stream
  13 * 3.lock/unlock streaming
  14 * 4.transmit command of EFW transaction
  15 * 5.receive response of EFW transaction
  16 *
  17 */
  18
  19#include "fireworks.h"
  20
  21static long
  22hwdep_read_resp_buf(struct snd_efw *efw, char __user *buf, long remained,
  23                    loff_t *offset)
  24{
  25        unsigned int length, till_end, type;
  26        struct snd_efw_transaction *t;
  27        u8 *pull_ptr;
  28        long count = 0;
  29
  30        if (remained < sizeof(type) + sizeof(struct snd_efw_transaction))
  31                return -ENOSPC;
  32
  33        /* data type is SNDRV_FIREWIRE_EVENT_EFW_RESPONSE */
  34        type = SNDRV_FIREWIRE_EVENT_EFW_RESPONSE;
  35        if (copy_to_user(buf, &type, sizeof(type)))
  36                return -EFAULT;
  37        remained -= sizeof(type);
  38        buf += sizeof(type);
  39
  40        /* write into buffer as many responses as possible */
  41        spin_lock_irq(&efw->lock);
  42
  43        /*
  44         * When another task reaches here during this task's access to user
  45         * space, it picks up current position in buffer and can read the same
  46         * series of responses.
  47         */
  48        pull_ptr = efw->pull_ptr;
  49
  50        while (efw->push_ptr != pull_ptr) {
  51                t = (struct snd_efw_transaction *)(pull_ptr);
  52                length = be32_to_cpu(t->length) * sizeof(__be32);
  53
  54                /* confirm enough space for this response */
  55                if (remained < length)
  56                        break;
  57
  58                /* copy from ring buffer to user buffer */
  59                while (length > 0) {
  60                        till_end = snd_efw_resp_buf_size -
  61                                (unsigned int)(pull_ptr - efw->resp_buf);
  62                        till_end = min_t(unsigned int, length, till_end);
  63
  64                        spin_unlock_irq(&efw->lock);
  65
  66                        if (copy_to_user(buf, pull_ptr, till_end))
  67                                return -EFAULT;
  68
  69                        spin_lock_irq(&efw->lock);
  70
  71                        pull_ptr += till_end;
  72                        if (pull_ptr >= efw->resp_buf + snd_efw_resp_buf_size)
  73                                pull_ptr -= snd_efw_resp_buf_size;
  74
  75                        length -= till_end;
  76                        buf += till_end;
  77                        count += till_end;
  78                        remained -= till_end;
  79                }
  80        }
  81
  82        /*
  83         * All of tasks can read from the buffer nearly simultaneously, but the
  84         * last position for each task is different depending on the length of
  85         * given buffer. Here, for simplicity, a position of buffer is set by
  86         * the latest task. It's better for a listening application to allow one
  87         * thread to read from the buffer. Unless, each task can read different
  88         * sequence of responses depending on variation of buffer length.
  89         */
  90        efw->pull_ptr = pull_ptr;
  91
  92        spin_unlock_irq(&efw->lock);
  93
  94        return count;
  95}
  96
  97static long
  98hwdep_read_locked(struct snd_efw *efw, char __user *buf, long count,
  99                  loff_t *offset)
 100{
 101        union snd_firewire_event event = {
 102                .lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,
 103        };
 104
 105        spin_lock_irq(&efw->lock);
 106
 107        event.lock_status.status = (efw->dev_lock_count > 0);
 108        efw->dev_lock_changed = false;
 109
 110        spin_unlock_irq(&efw->lock);
 111
 112        count = min_t(long, count, sizeof(event.lock_status));
 113
 114        if (copy_to_user(buf, &event, count))
 115                return -EFAULT;
 116
 117        return count;
 118}
 119
 120static long
 121hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
 122           loff_t *offset)
 123{
 124        struct snd_efw *efw = hwdep->private_data;
 125        DEFINE_WAIT(wait);
 126        bool dev_lock_changed;
 127        bool queued;
 128
 129        spin_lock_irq(&efw->lock);
 130
 131        dev_lock_changed = efw->dev_lock_changed;
 132        queued = efw->push_ptr != efw->pull_ptr;
 133
 134        while (!dev_lock_changed && !queued) {
 135                prepare_to_wait(&efw->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
 136                spin_unlock_irq(&efw->lock);
 137                schedule();
 138                finish_wait(&efw->hwdep_wait, &wait);
 139                if (signal_pending(current))
 140                        return -ERESTARTSYS;
 141                spin_lock_irq(&efw->lock);
 142                dev_lock_changed = efw->dev_lock_changed;
 143                queued = efw->push_ptr != efw->pull_ptr;
 144        }
 145
 146        spin_unlock_irq(&efw->lock);
 147
 148        if (dev_lock_changed)
 149                count = hwdep_read_locked(efw, buf, count, offset);
 150        else if (queued)
 151                count = hwdep_read_resp_buf(efw, buf, count, offset);
 152
 153        return count;
 154}
 155
 156static long
 157hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count,
 158            loff_t *offset)
 159{
 160        struct snd_efw *efw = hwdep->private_data;
 161        u32 seqnum;
 162        u8 *buf;
 163
 164        if (count < sizeof(struct snd_efw_transaction) ||
 165            SND_EFW_RESPONSE_MAXIMUM_BYTES < count)
 166                return -EINVAL;
 167
 168        buf = memdup_user(data, count);
 169        if (IS_ERR(buf))
 170                return PTR_ERR(buf);
 171
 172        /* check seqnum is not for kernel-land */
 173        seqnum = be32_to_cpu(((struct snd_efw_transaction *)buf)->seqnum);
 174        if (seqnum > SND_EFW_TRANSACTION_USER_SEQNUM_MAX) {
 175                count = -EINVAL;
 176                goto end;
 177        }
 178
 179        if (snd_efw_transaction_cmd(efw->unit, buf, count) < 0)
 180                count = -EIO;
 181end:
 182        kfree(buf);
 183        return count;
 184}
 185
 186static __poll_t
 187hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait)
 188{
 189        struct snd_efw *efw = hwdep->private_data;
 190        __poll_t events;
 191
 192        poll_wait(file, &efw->hwdep_wait, wait);
 193
 194        spin_lock_irq(&efw->lock);
 195        if (efw->dev_lock_changed || efw->pull_ptr != efw->push_ptr)
 196                events = EPOLLIN | EPOLLRDNORM;
 197        else
 198                events = 0;
 199        spin_unlock_irq(&efw->lock);
 200
 201        return events | EPOLLOUT;
 202}
 203
 204static int
 205hwdep_get_info(struct snd_efw *efw, void __user *arg)
 206{
 207        struct fw_device *dev = fw_parent_device(efw->unit);
 208        struct snd_firewire_get_info info;
 209
 210        memset(&info, 0, sizeof(info));
 211        info.type = SNDRV_FIREWIRE_TYPE_FIREWORKS;
 212        info.card = dev->card->index;
 213        *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
 214        *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
 215        strscpy(info.device_name, dev_name(&dev->device),
 216                sizeof(info.device_name));
 217
 218        if (copy_to_user(arg, &info, sizeof(info)))
 219                return -EFAULT;
 220
 221        return 0;
 222}
 223
 224static int
 225hwdep_lock(struct snd_efw *efw)
 226{
 227        int err;
 228
 229        spin_lock_irq(&efw->lock);
 230
 231        if (efw->dev_lock_count == 0) {
 232                efw->dev_lock_count = -1;
 233                err = 0;
 234        } else {
 235                err = -EBUSY;
 236        }
 237
 238        spin_unlock_irq(&efw->lock);
 239
 240        return err;
 241}
 242
 243static int
 244hwdep_unlock(struct snd_efw *efw)
 245{
 246        int err;
 247
 248        spin_lock_irq(&efw->lock);
 249
 250        if (efw->dev_lock_count == -1) {
 251                efw->dev_lock_count = 0;
 252                err = 0;
 253        } else {
 254                err = -EBADFD;
 255        }
 256
 257        spin_unlock_irq(&efw->lock);
 258
 259        return err;
 260}
 261
 262static int
 263hwdep_release(struct snd_hwdep *hwdep, struct file *file)
 264{
 265        struct snd_efw *efw = hwdep->private_data;
 266
 267        spin_lock_irq(&efw->lock);
 268        if (efw->dev_lock_count == -1)
 269                efw->dev_lock_count = 0;
 270        spin_unlock_irq(&efw->lock);
 271
 272        return 0;
 273}
 274
 275static int
 276hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
 277            unsigned int cmd, unsigned long arg)
 278{
 279        struct snd_efw *efw = hwdep->private_data;
 280
 281        switch (cmd) {
 282        case SNDRV_FIREWIRE_IOCTL_GET_INFO:
 283                return hwdep_get_info(efw, (void __user *)arg);
 284        case SNDRV_FIREWIRE_IOCTL_LOCK:
 285                return hwdep_lock(efw);
 286        case SNDRV_FIREWIRE_IOCTL_UNLOCK:
 287                return hwdep_unlock(efw);
 288        default:
 289                return -ENOIOCTLCMD;
 290        }
 291}
 292
 293#ifdef CONFIG_COMPAT
 294static int
 295hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
 296                   unsigned int cmd, unsigned long arg)
 297{
 298        return hwdep_ioctl(hwdep, file, cmd,
 299                           (unsigned long)compat_ptr(arg));
 300}
 301#else
 302#define hwdep_compat_ioctl NULL
 303#endif
 304
 305int snd_efw_create_hwdep_device(struct snd_efw *efw)
 306{
 307        static const struct snd_hwdep_ops ops = {
 308                .read           = hwdep_read,
 309                .write          = hwdep_write,
 310                .release        = hwdep_release,
 311                .poll           = hwdep_poll,
 312                .ioctl          = hwdep_ioctl,
 313                .ioctl_compat   = hwdep_compat_ioctl,
 314        };
 315        struct snd_hwdep *hwdep;
 316        int err;
 317
 318        err = snd_hwdep_new(efw->card, "Fireworks", 0, &hwdep);
 319        if (err < 0)
 320                goto end;
 321        strcpy(hwdep->name, "Fireworks");
 322        hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREWORKS;
 323        hwdep->ops = ops;
 324        hwdep->private_data = efw;
 325        hwdep->exclusive = true;
 326end:
 327        return err;
 328}
 329
 330