188 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * oxfw_hwdep.c - a part of driver for OXFW970/971 based devices4 *5 * Copyright (c) 2014 Takashi Sakamoto6 */7 8/*9 * This codes give three functionality.10 *11 * 1.get firewire node information12 * 2.get notification about starting/stopping stream13 * 3.lock/unlock stream14 */15 16#include "oxfw.h"17 18static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,19 loff_t *offset)20{21 struct snd_oxfw *oxfw = hwdep->private_data;22 DEFINE_WAIT(wait);23 union snd_firewire_event event;24 25 spin_lock_irq(&oxfw->lock);26 27 while (!oxfw->dev_lock_changed) {28 prepare_to_wait(&oxfw->hwdep_wait, &wait, TASK_INTERRUPTIBLE);29 spin_unlock_irq(&oxfw->lock);30 schedule();31 finish_wait(&oxfw->hwdep_wait, &wait);32 if (signal_pending(current))33 return -ERESTARTSYS;34 spin_lock_irq(&oxfw->lock);35 }36 37 memset(&event, 0, sizeof(event));38 event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;39 event.lock_status.status = (oxfw->dev_lock_count > 0);40 oxfw->dev_lock_changed = false;41 42 count = min_t(long, count, sizeof(event.lock_status));43 44 spin_unlock_irq(&oxfw->lock);45 46 if (copy_to_user(buf, &event, count))47 return -EFAULT;48 49 return count;50}51 52static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,53 poll_table *wait)54{55 struct snd_oxfw *oxfw = hwdep->private_data;56 __poll_t events;57 58 poll_wait(file, &oxfw->hwdep_wait, wait);59 60 spin_lock_irq(&oxfw->lock);61 if (oxfw->dev_lock_changed)62 events = EPOLLIN | EPOLLRDNORM;63 else64 events = 0;65 spin_unlock_irq(&oxfw->lock);66 67 return events;68}69 70static int hwdep_get_info(struct snd_oxfw *oxfw, void __user *arg)71{72 struct fw_device *dev = fw_parent_device(oxfw->unit);73 struct snd_firewire_get_info info;74 75 memset(&info, 0, sizeof(info));76 info.type = SNDRV_FIREWIRE_TYPE_OXFW;77 info.card = dev->card->index;78 *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);79 *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);80 strscpy(info.device_name, dev_name(&dev->device),81 sizeof(info.device_name));82 83 if (copy_to_user(arg, &info, sizeof(info)))84 return -EFAULT;85 86 return 0;87}88 89static int hwdep_lock(struct snd_oxfw *oxfw)90{91 int err;92 93 spin_lock_irq(&oxfw->lock);94 95 if (oxfw->dev_lock_count == 0) {96 oxfw->dev_lock_count = -1;97 err = 0;98 } else {99 err = -EBUSY;100 }101 102 spin_unlock_irq(&oxfw->lock);103 104 return err;105}106 107static int hwdep_unlock(struct snd_oxfw *oxfw)108{109 int err;110 111 spin_lock_irq(&oxfw->lock);112 113 if (oxfw->dev_lock_count == -1) {114 oxfw->dev_lock_count = 0;115 err = 0;116 } else {117 err = -EBADFD;118 }119 120 spin_unlock_irq(&oxfw->lock);121 122 return err;123}124 125static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)126{127 struct snd_oxfw *oxfw = hwdep->private_data;128 129 spin_lock_irq(&oxfw->lock);130 if (oxfw->dev_lock_count == -1)131 oxfw->dev_lock_count = 0;132 spin_unlock_irq(&oxfw->lock);133 134 return 0;135}136 137static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,138 unsigned int cmd, unsigned long arg)139{140 struct snd_oxfw *oxfw = hwdep->private_data;141 142 switch (cmd) {143 case SNDRV_FIREWIRE_IOCTL_GET_INFO:144 return hwdep_get_info(oxfw, (void __user *)arg);145 case SNDRV_FIREWIRE_IOCTL_LOCK:146 return hwdep_lock(oxfw);147 case SNDRV_FIREWIRE_IOCTL_UNLOCK:148 return hwdep_unlock(oxfw);149 default:150 return -ENOIOCTLCMD;151 }152}153 154#ifdef CONFIG_COMPAT155static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,156 unsigned int cmd, unsigned long arg)157{158 return hwdep_ioctl(hwdep, file, cmd,159 (unsigned long)compat_ptr(arg));160}161#else162#define hwdep_compat_ioctl NULL163#endif164 165int snd_oxfw_create_hwdep(struct snd_oxfw *oxfw)166{167 static const struct snd_hwdep_ops hwdep_ops = {168 .read = hwdep_read,169 .release = hwdep_release,170 .poll = hwdep_poll,171 .ioctl = hwdep_ioctl,172 .ioctl_compat = hwdep_compat_ioctl,173 };174 struct snd_hwdep *hwdep;175 int err;176 177 err = snd_hwdep_new(oxfw->card, oxfw->card->driver, 0, &hwdep);178 if (err < 0)179 goto end;180 strcpy(hwdep->name, oxfw->card->driver);181 hwdep->iface = SNDRV_HWDEP_IFACE_FW_OXFW;182 hwdep->ops = hwdep_ops;183 hwdep->private_data = oxfw;184 hwdep->exclusive = true;185end:186 return err;187}188