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