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