root/sound/firewire/tascam/tascam-hwdep.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. tscm_hwdep_read_locked
  2. tscm_hwdep_read_queue
  3. hwdep_read
  4. hwdep_poll
  5. hwdep_get_info
  6. hwdep_lock
  7. hwdep_unlock
  8. tscm_hwdep_state
  9. hwdep_release
  10. hwdep_ioctl
  11. hwdep_compat_ioctl
  12. snd_tscm_create_hwdep_device

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * tascam-hwdep.c - a part of driver for TASCAM FireWire series
   4  *
   5  * Copyright (c) 2015 Takashi Sakamoto
   6  */
   7 
   8 /*
   9  * This codes give three functionality.
  10  *
  11  * 1.get firewire node information
  12  * 2.get notification about starting/stopping stream
  13  * 3.lock/unlock stream
  14  */
  15 
  16 #include "tascam.h"
  17 
  18 static long tscm_hwdep_read_locked(struct snd_tscm *tscm, char __user *buf,
  19                                    long count, loff_t *offset)
  20 {
  21         struct snd_firewire_event_lock_status event = {
  22                 .type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,
  23         };
  24 
  25         event.status = (tscm->dev_lock_count > 0);
  26         tscm->dev_lock_changed = false;
  27         count = min_t(long, count, sizeof(event));
  28 
  29         spin_unlock_irq(&tscm->lock);
  30 
  31         if (copy_to_user(buf, &event, count))
  32                 return -EFAULT;
  33 
  34         return count;
  35 }
  36 
  37 static long tscm_hwdep_read_queue(struct snd_tscm *tscm, char __user *buf,
  38                                   long remained, loff_t *offset)
  39 {
  40         char __user *pos = buf;
  41         unsigned int type = SNDRV_FIREWIRE_EVENT_TASCAM_CONTROL;
  42         struct snd_firewire_tascam_change *entries = tscm->queue;
  43         long count;
  44 
  45         // At least, one control event can be copied.
  46         if (remained < sizeof(type) + sizeof(*entries)) {
  47                 spin_unlock_irq(&tscm->lock);
  48                 return -EINVAL;
  49         }
  50 
  51         // Copy the type field later.
  52         count = sizeof(type);
  53         remained -= sizeof(type);
  54         pos += sizeof(type);
  55 
  56         while (true) {
  57                 unsigned int head_pos;
  58                 unsigned int tail_pos;
  59                 unsigned int length;
  60 
  61                 if (tscm->pull_pos == tscm->push_pos)
  62                         break;
  63                 else if (tscm->pull_pos < tscm->push_pos)
  64                         tail_pos = tscm->push_pos;
  65                 else
  66                         tail_pos = SND_TSCM_QUEUE_COUNT;
  67                 head_pos = tscm->pull_pos;
  68 
  69                 length = (tail_pos - head_pos) * sizeof(*entries);
  70                 if (remained < length)
  71                         length = rounddown(remained, sizeof(*entries));
  72                 if (length == 0)
  73                         break;
  74 
  75                 spin_unlock_irq(&tscm->lock);
  76                 if (copy_to_user(pos, &entries[head_pos], length))
  77                         return -EFAULT;
  78 
  79                 spin_lock_irq(&tscm->lock);
  80 
  81                 tscm->pull_pos = tail_pos % SND_TSCM_QUEUE_COUNT;
  82 
  83                 count += length;
  84                 remained -= length;
  85                 pos += length;
  86         }
  87 
  88         spin_unlock_irq(&tscm->lock);
  89 
  90         if (copy_to_user(buf, &type, sizeof(type)))
  91                 return -EFAULT;
  92 
  93         return count;
  94 }
  95 
  96 static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
  97                        loff_t *offset)
  98 {
  99         struct snd_tscm *tscm = hwdep->private_data;
 100         DEFINE_WAIT(wait);
 101 
 102         spin_lock_irq(&tscm->lock);
 103 
 104         while (!tscm->dev_lock_changed && tscm->push_pos == tscm->pull_pos) {
 105                 prepare_to_wait(&tscm->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
 106                 spin_unlock_irq(&tscm->lock);
 107                 schedule();
 108                 finish_wait(&tscm->hwdep_wait, &wait);
 109                 if (signal_pending(current))
 110                         return -ERESTARTSYS;
 111                 spin_lock_irq(&tscm->lock);
 112         }
 113 
 114         // NOTE: The acquired lock should be released in callee side.
 115         if (tscm->dev_lock_changed) {
 116                 count = tscm_hwdep_read_locked(tscm, buf, count, offset);
 117         } else if (tscm->push_pos != tscm->pull_pos) {
 118                 count = tscm_hwdep_read_queue(tscm, buf, count, offset);
 119         } else {
 120                 spin_unlock_irq(&tscm->lock);
 121                 count = 0;
 122         }
 123 
 124         return count;
 125 }
 126 
 127 static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
 128                                poll_table *wait)
 129 {
 130         struct snd_tscm *tscm = hwdep->private_data;
 131         __poll_t events;
 132 
 133         poll_wait(file, &tscm->hwdep_wait, wait);
 134 
 135         spin_lock_irq(&tscm->lock);
 136         if (tscm->dev_lock_changed || tscm->push_pos != tscm->pull_pos)
 137                 events = EPOLLIN | EPOLLRDNORM;
 138         else
 139                 events = 0;
 140         spin_unlock_irq(&tscm->lock);
 141 
 142         return events;
 143 }
 144 
 145 static int hwdep_get_info(struct snd_tscm *tscm, void __user *arg)
 146 {
 147         struct fw_device *dev = fw_parent_device(tscm->unit);
 148         struct snd_firewire_get_info info;
 149 
 150         memset(&info, 0, sizeof(info));
 151         info.type = SNDRV_FIREWIRE_TYPE_TASCAM;
 152         info.card = dev->card->index;
 153         *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
 154         *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
 155         strlcpy(info.device_name, dev_name(&dev->device),
 156                 sizeof(info.device_name));
 157 
 158         if (copy_to_user(arg, &info, sizeof(info)))
 159                 return -EFAULT;
 160 
 161         return 0;
 162 }
 163 
 164 static int hwdep_lock(struct snd_tscm *tscm)
 165 {
 166         int err;
 167 
 168         spin_lock_irq(&tscm->lock);
 169 
 170         if (tscm->dev_lock_count == 0) {
 171                 tscm->dev_lock_count = -1;
 172                 err = 0;
 173         } else {
 174                 err = -EBUSY;
 175         }
 176 
 177         spin_unlock_irq(&tscm->lock);
 178 
 179         return err;
 180 }
 181 
 182 static int hwdep_unlock(struct snd_tscm *tscm)
 183 {
 184         int err;
 185 
 186         spin_lock_irq(&tscm->lock);
 187 
 188         if (tscm->dev_lock_count == -1) {
 189                 tscm->dev_lock_count = 0;
 190                 err = 0;
 191         } else {
 192                 err = -EBADFD;
 193         }
 194 
 195         spin_unlock_irq(&tscm->lock);
 196 
 197         return err;
 198 }
 199 
 200 static int tscm_hwdep_state(struct snd_tscm *tscm, void __user *arg)
 201 {
 202         if (copy_to_user(arg, tscm->state, sizeof(tscm->state)))
 203                 return -EFAULT;
 204 
 205         return 0;
 206 }
 207 
 208 static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
 209 {
 210         struct snd_tscm *tscm = hwdep->private_data;
 211 
 212         spin_lock_irq(&tscm->lock);
 213         if (tscm->dev_lock_count == -1)
 214                 tscm->dev_lock_count = 0;
 215         spin_unlock_irq(&tscm->lock);
 216 
 217         return 0;
 218 }
 219 
 220 static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
 221             unsigned int cmd, unsigned long arg)
 222 {
 223         struct snd_tscm *tscm = hwdep->private_data;
 224 
 225         switch (cmd) {
 226         case SNDRV_FIREWIRE_IOCTL_GET_INFO:
 227                 return hwdep_get_info(tscm, (void __user *)arg);
 228         case SNDRV_FIREWIRE_IOCTL_LOCK:
 229                 return hwdep_lock(tscm);
 230         case SNDRV_FIREWIRE_IOCTL_UNLOCK:
 231                 return hwdep_unlock(tscm);
 232         case SNDRV_FIREWIRE_IOCTL_TASCAM_STATE:
 233                 return tscm_hwdep_state(tscm, (void __user *)arg);
 234         default:
 235                 return -ENOIOCTLCMD;
 236         }
 237 }
 238 
 239 #ifdef CONFIG_COMPAT
 240 static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
 241                               unsigned int cmd, unsigned long arg)
 242 {
 243         return hwdep_ioctl(hwdep, file, cmd,
 244                            (unsigned long)compat_ptr(arg));
 245 }
 246 #else
 247 #define hwdep_compat_ioctl NULL
 248 #endif
 249 
 250 int snd_tscm_create_hwdep_device(struct snd_tscm *tscm)
 251 {
 252         static const struct snd_hwdep_ops ops = {
 253                 .read           = hwdep_read,
 254                 .release        = hwdep_release,
 255                 .poll           = hwdep_poll,
 256                 .ioctl          = hwdep_ioctl,
 257                 .ioctl_compat   = hwdep_compat_ioctl,
 258         };
 259         struct snd_hwdep *hwdep;
 260         int err;
 261 
 262         err = snd_hwdep_new(tscm->card, "Tascam", 0, &hwdep);
 263         if (err < 0)
 264                 return err;
 265 
 266         strcpy(hwdep->name, "Tascam");
 267         hwdep->iface = SNDRV_HWDEP_IFACE_FW_TASCAM;
 268         hwdep->ops = ops;
 269         hwdep->private_data = tscm;
 270         hwdep->exclusive = true;
 271 
 272         tscm->hwdep = hwdep;
 273 
 274         return err;
 275 }

/* [<][>][^][v][top][bottom][index][help] */