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 __poll_t events; 67 68 poll_wait(file, &dg00x->hwdep_wait, wait); 69 70 spin_lock_irq(&dg00x->lock); 71 if (dg00x->dev_lock_changed || dg00x->msg) 72 events = EPOLLIN | EPOLLRDNORM; 73 else 74 events = 0; 75 spin_unlock_irq(&dg00x->lock); 76 77 return events; 78 } 79 80 static int hwdep_get_info(struct snd_dg00x *dg00x, void __user *arg) 81 { 82 struct fw_device *dev = fw_parent_device(dg00x->unit); 83 struct snd_firewire_get_info info; 84 85 memset(&info, 0, sizeof(info)); 86 info.type = SNDRV_FIREWIRE_TYPE_DIGI00X; 87 info.card = dev->card->index; 88 *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); 89 *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); 90 strlcpy(info.device_name, dev_name(&dev->device), 91 sizeof(info.device_name)); 92 93 if (copy_to_user(arg, &info, sizeof(info))) 94 return -EFAULT; 95 96 return 0; 97 } 98 99 static int hwdep_lock(struct snd_dg00x *dg00x) 100 { 101 int err; 102 103 spin_lock_irq(&dg00x->lock); 104 105 if (dg00x->dev_lock_count == 0) { 106 dg00x->dev_lock_count = -1; 107 err = 0; 108 } else { 109 err = -EBUSY; 110 } 111 112 spin_unlock_irq(&dg00x->lock); 113 114 return err; 115 } 116 117 static int hwdep_unlock(struct snd_dg00x *dg00x) 118 { 119 int err; 120 121 spin_lock_irq(&dg00x->lock); 122 123 if (dg00x->dev_lock_count == -1) { 124 dg00x->dev_lock_count = 0; 125 err = 0; 126 } else { 127 err = -EBADFD; 128 } 129 130 spin_unlock_irq(&dg00x->lock); 131 132 return err; 133 } 134 135 static int hwdep_release(struct snd_hwdep *hwdep, struct file *file) 136 { 137 struct snd_dg00x *dg00x = hwdep->private_data; 138 139 spin_lock_irq(&dg00x->lock); 140 if (dg00x->dev_lock_count == -1) 141 dg00x->dev_lock_count = 0; 142 spin_unlock_irq(&dg00x->lock); 143 144 return 0; 145 } 146 147 static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, 148 unsigned int cmd, unsigned long arg) 149 { 150 struct snd_dg00x *dg00x = hwdep->private_data; 151 152 switch (cmd) { 153 case SNDRV_FIREWIRE_IOCTL_GET_INFO: 154 return hwdep_get_info(dg00x, (void __user *)arg); 155 case SNDRV_FIREWIRE_IOCTL_LOCK: 156 return hwdep_lock(dg00x); 157 case SNDRV_FIREWIRE_IOCTL_UNLOCK: 158 return hwdep_unlock(dg00x); 159 default: 160 return -ENOIOCTLCMD; 161 } 162 } 163 164 #ifdef CONFIG_COMPAT 165 static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, 166 unsigned int cmd, unsigned long arg) 167 { 168 return hwdep_ioctl(hwdep, file, cmd, 169 (unsigned long)compat_ptr(arg)); 170 } 171 #else 172 #define hwdep_compat_ioctl NULL 173 #endif 174 175 int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x) 176 { 177 static const struct snd_hwdep_ops ops = { 178 .read = hwdep_read, 179 .release = hwdep_release, 180 .poll = hwdep_poll, 181 .ioctl = hwdep_ioctl, 182 .ioctl_compat = hwdep_compat_ioctl, 183 }; 184 struct snd_hwdep *hwdep; 185 int err; 186 187 err = snd_hwdep_new(dg00x->card, "Digi00x", 0, &hwdep); 188 if (err < 0) 189 return err; 190 191 strcpy(hwdep->name, "Digi00x"); 192 hwdep->iface = SNDRV_HWDEP_IFACE_FW_DIGI00X; 193 hwdep->ops = ops; 194 hwdep->private_data = dg00x; 195 hwdep->exclusive = true; 196 197 return err; 198 } 199