root/sound/firewire/fireworks/fireworks_hwdep.c

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

DEFINITIONS

This source file includes following definitions.
  1. hwdep_read_resp_buf
  2. hwdep_read_locked
  3. hwdep_read
  4. hwdep_write
  5. hwdep_poll
  6. hwdep_get_info
  7. hwdep_lock
  8. hwdep_unlock
  9. hwdep_release
  10. hwdep_ioctl
  11. hwdep_compat_ioctl
  12. snd_efw_create_hwdep_device

   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 
  21 static long
  22 hwdep_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 
  97 static long
  98 hwdep_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 
 120 static long
 121 hwdep_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 
 156 static long
 157 hwdep_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;
 181 end:
 182         kfree(buf);
 183         return count;
 184 }
 185 
 186 static __poll_t
 187 hwdep_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 
 204 static int
 205 hwdep_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         strlcpy(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 
 224 static int
 225 hwdep_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 
 243 static int
 244 hwdep_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 
 262 static int
 263 hwdep_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 
 275 static int
 276 hwdep_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
 294 static int
 295 hwdep_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 
 305 int 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;
 326 end:
 327         return err;
 328 }
 329 

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