1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * dice_hwdep.c - a part of driver for DICE based devices 4 * 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 11 static 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 54 static __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 59 poll_wait(file, &dice->hwdep_wait, wait); 60 61 guard(spinlock_irq)(&dice->lock); 62 if (dice->dev_lock_changed || dice->notification_bits != 0) 63 return EPOLLIN | EPOLLRDNORM; 64 else 65 return 0; 66 } 67 68 static int hwdep_get_info(struct snd_dice *dice, void __user *arg) 69 { 70 struct fw_device *dev = fw_parent_device(dice->unit); 71 struct snd_firewire_get_info info; 72 73 memset(&info, 0, sizeof(info)); 74 info.type = SNDRV_FIREWIRE_TYPE_DICE; 75 info.card = dev->card->index; 76 *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); 77 *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); 78 strscpy(info.device_name, dev_name(&dev->device), 79 sizeof(info.device_name)); 80 81 if (copy_to_user(arg, &info, sizeof(info))) 82 return -EFAULT; 83 84 return 0; 85 } 86 87 static int hwdep_lock(struct snd_dice *dice) 88 { 89 guard(spinlock_irq)(&dice->lock); 90 91 if (dice->dev_lock_count == 0) { 92 dice->dev_lock_count = -1; 93 return 0; 94 } else { 95 return -EBUSY; 96 } 97 } 98 99 static int hwdep_unlock(struct snd_dice *dice) 100 { 101 guard(spinlock_irq)(&dice->lock); 102 103 if (dice->dev_lock_count == -1) { 104 dice->dev_lock_count = 0; 105 return 0; 106 } else { 107 return -EBADFD; 108 } 109 } 110 111 static int hwdep_release(struct snd_hwdep *hwdep, struct file *file) 112 { 113 struct snd_dice *dice = hwdep->private_data; 114 115 guard(spinlock_irq)(&dice->lock); 116 if (dice->dev_lock_count == -1) 117 dice->dev_lock_count = 0; 118 119 return 0; 120 } 121 122 static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, 123 unsigned int cmd, unsigned long arg) 124 { 125 struct snd_dice *dice = hwdep->private_data; 126 127 switch (cmd) { 128 case SNDRV_FIREWIRE_IOCTL_GET_INFO: 129 return hwdep_get_info(dice, (void __user *)arg); 130 case SNDRV_FIREWIRE_IOCTL_LOCK: 131 return hwdep_lock(dice); 132 case SNDRV_FIREWIRE_IOCTL_UNLOCK: 133 return hwdep_unlock(dice); 134 default: 135 return -ENOIOCTLCMD; 136 } 137 } 138 139 #ifdef CONFIG_COMPAT 140 static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, 141 unsigned int cmd, unsigned long arg) 142 { 143 return hwdep_ioctl(hwdep, file, cmd, 144 (unsigned long)compat_ptr(arg)); 145 } 146 #else 147 #define hwdep_compat_ioctl NULL 148 #endif 149 150 int snd_dice_create_hwdep(struct snd_dice *dice) 151 { 152 static const struct snd_hwdep_ops ops = { 153 .read = hwdep_read, 154 .release = hwdep_release, 155 .poll = hwdep_poll, 156 .ioctl = hwdep_ioctl, 157 .ioctl_compat = hwdep_compat_ioctl, 158 }; 159 struct snd_hwdep *hwdep; 160 int err; 161 162 err = snd_hwdep_new(dice->card, "DICE", 0, &hwdep); 163 if (err < 0) 164 return err; 165 strscpy(hwdep->name, "DICE"); 166 hwdep->iface = SNDRV_HWDEP_IFACE_FW_DICE; 167 hwdep->ops = ops; 168 hwdep->private_data = dice; 169 hwdep->exclusive = true; 170 171 return 0; 172 } 173