1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * digi00x-hwdep.c - a part of driver for Digidesign Digi 002/003 family 4 * 5 * Copyright (c) 2014-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 * 4.get asynchronous messaging 15 */ 16 17 #include "digi00x.h" 18 19 static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, 20 loff_t *offset) 21 { 22 struct snd_dg00x *dg00x = hwdep->private_data; 23 DEFINE_WAIT(wait); 24 union snd_firewire_event event; 25 26 spin_lock_irq(&dg00x->lock); 27 28 while (!dg00x->dev_lock_changed && dg00x->msg == 0) { 29 prepare_to_wait(&dg00x->hwdep_wait, &wait, TASK_INTERRUPTIBLE); 30 spin_unlock_irq(&dg00x->lock); 31 schedule(); 32 finish_wait(&dg00x->hwdep_wait, &wait); 33 if (signal_pending(current)) 34 return -ERESTARTSYS; 35 spin_lock_irq(&dg00x->lock); 36 } 37 38 memset(&event, 0, sizeof(event)); 39 if (dg00x->dev_lock_changed) { 40 event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; 41 event.lock_status.status = (dg00x->dev_lock_count > 0); 42 dg00x->dev_lock_changed = false; 43 44 count = min_t(long, count, sizeof(event.lock_status)); 45 } else { 46 event.digi00x_message.type = 47 SNDRV_FIREWIRE_EVENT_DIGI00X_MESSAGE; 48 event.digi00x_message.message = dg00x->msg; 49 dg00x->msg = 0; 50 51 count = min_t(long, count, sizeof(event.digi00x_message)); 52 } 53 54 spin_unlock_irq(&dg00x->lock); 55 56 if (copy_to_user(buf, &event, count)) 57 return -EFAULT; 58 59 return count; 60 } 61 62 static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file, 63 poll_table *wait) 64 { 65 struct snd_dg00x *dg00x = hwdep->private_data; 66 67 poll_wait(file, &dg00x->hwdep_wait, wait); 68 69 guard(spinlock_irq)(&dg00x->lock); 70 if (dg00x->dev_lock_changed || dg00x->msg) 71 return EPOLLIN | EPOLLRDNORM; 72 else 73 return 0; 74 } 75 76 static int hwdep_get_info(struct snd_dg00x *dg00x, void __user *arg) 77 { 78 struct fw_device *dev = fw_parent_device(dg00x->unit); 79 struct snd_firewire_get_info info; 80 81 memset(&info, 0, sizeof(info)); 82 info.type = SNDRV_FIREWIRE_TYPE_DIGI00X; 83 info.card = dev->card->index; 84 *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); 85 *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); 86 strscpy(info.device_name, dev_name(&dev->device), 87 sizeof(info.device_name)); 88 89 if (copy_to_user(arg, &info, sizeof(info))) 90 return -EFAULT; 91 92 return 0; 93 } 94 95 static int hwdep_lock(struct snd_dg00x *dg00x) 96 { 97 guard(spinlock_irq)(&dg00x->lock); 98 99 if (dg00x->dev_lock_count == 0) { 100 dg00x->dev_lock_count = -1; 101 return 0; 102 } else { 103 return -EBUSY; 104 } 105 } 106 107 static int hwdep_unlock(struct snd_dg00x *dg00x) 108 { 109 guard(spinlock_irq)(&dg00x->lock); 110 111 if (dg00x->dev_lock_count == -1) { 112 dg00x->dev_lock_count = 0; 113 return 0; 114 } else { 115 return -EBADFD; 116 } 117 } 118 119 static int hwdep_release(struct snd_hwdep *hwdep, struct file *file) 120 { 121 struct snd_dg00x *dg00x = hwdep->private_data; 122 123 guard(spinlock_irq)(&dg00x->lock); 124 if (dg00x->dev_lock_count == -1) 125 dg00x->dev_lock_count = 0; 126 127 return 0; 128 } 129 130 static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, 131 unsigned int cmd, unsigned long arg) 132 { 133 struct snd_dg00x *dg00x = hwdep->private_data; 134 135 switch (cmd) { 136 case SNDRV_FIREWIRE_IOCTL_GET_INFO: 137 return hwdep_get_info(dg00x, (void __user *)arg); 138 case SNDRV_FIREWIRE_IOCTL_LOCK: 139 return hwdep_lock(dg00x); 140 case SNDRV_FIREWIRE_IOCTL_UNLOCK: 141 return hwdep_unlock(dg00x); 142 default: 143 return -ENOIOCTLCMD; 144 } 145 } 146 147 #ifdef CONFIG_COMPAT 148 static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, 149 unsigned int cmd, unsigned long arg) 150 { 151 return hwdep_ioctl(hwdep, file, cmd, 152 (unsigned long)compat_ptr(arg)); 153 } 154 #else 155 #define hwdep_compat_ioctl NULL 156 #endif 157 158 int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x) 159 { 160 static const struct snd_hwdep_ops ops = { 161 .read = hwdep_read, 162 .release = hwdep_release, 163 .poll = hwdep_poll, 164 .ioctl = hwdep_ioctl, 165 .ioctl_compat = hwdep_compat_ioctl, 166 }; 167 struct snd_hwdep *hwdep; 168 int err; 169 170 err = snd_hwdep_new(dg00x->card, "Digi00x", 0, &hwdep); 171 if (err < 0) 172 return err; 173 174 strscpy(hwdep->name, "Digi00x"); 175 hwdep->iface = SNDRV_HWDEP_IFACE_FW_DIGI00X; 176 hwdep->ops = ops; 177 hwdep->private_data = dg00x; 178 hwdep->exclusive = true; 179 180 return err; 181 } 182