1 /* 2 * motu-hwdep.c - a part of driver for MOTU FireWire series 3 * 4 * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp> 5 * 6 * Licensed under the terms of the GNU General Public License, version 2. 7 */ 8 9 /* 10 * This codes have five functionalities. 11 * 12 * 1.get information about firewire node 13 * 2.get notification about starting/stopping stream 14 * 3.lock/unlock streaming 15 * 16 */ 17 18 #include "motu.h" 19 20 static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, 21 loff_t *offset) 22 { 23 struct snd_motu *motu = hwdep->private_data; 24 DEFINE_WAIT(wait); 25 union snd_firewire_event event; 26 27 spin_lock_irq(&motu->lock); 28 29 while (!motu->dev_lock_changed && motu->msg == 0) { 30 prepare_to_wait(&motu->hwdep_wait, &wait, TASK_INTERRUPTIBLE); 31 spin_unlock_irq(&motu->lock); 32 schedule(); 33 finish_wait(&motu->hwdep_wait, &wait); 34 if (signal_pending(current)) 35 return -ERESTARTSYS; 36 spin_lock_irq(&motu->lock); 37 } 38 39 memset(&event, 0, sizeof(event)); 40 if (motu->dev_lock_changed) { 41 event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; 42 event.lock_status.status = (motu->dev_lock_count > 0); 43 motu->dev_lock_changed = false; 44 45 count = min_t(long, count, sizeof(event.lock_status)); 46 } else { 47 event.motu_notification.type = SNDRV_FIREWIRE_EVENT_MOTU_NOTIFICATION; 48 event.motu_notification.message = motu->msg; 49 motu->msg = 0; 50 51 count = min_t(long, count, sizeof(event.motu_notification)); 52 } 53 54 spin_unlock_irq(&motu->lock); 55 56 if (copy_to_user(buf, &event, count)) 57 return -EFAULT; 58 59 return count; 60 } 61 62 static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file, 63 poll_table *wait) 64 { 65 struct snd_motu *motu = hwdep->private_data; 66 unsigned int events; 67 68 poll_wait(file, &motu->hwdep_wait, wait); 69 70 spin_lock_irq(&motu->lock); 71 if (motu->dev_lock_changed || motu->msg) 72 events = POLLIN | POLLRDNORM; 73 else 74 events = 0; 75 spin_unlock_irq(&motu->lock); 76 77 return events | POLLOUT; 78 } 79 80 static int hwdep_get_info(struct snd_motu *motu, void __user *arg) 81 { 82 struct fw_device *dev = fw_parent_device(motu->unit); 83 struct snd_firewire_get_info info; 84 85 memset(&info, 0, sizeof(info)); 86 info.type = SNDRV_FIREWIRE_TYPE_MOTU; 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_motu *motu) 100 { 101 int err; 102 103 spin_lock_irq(&motu->lock); 104 105 if (motu->dev_lock_count == 0) { 106 motu->dev_lock_count = -1; 107 err = 0; 108 } else { 109 err = -EBUSY; 110 } 111 112 spin_unlock_irq(&motu->lock); 113 114 return err; 115 } 116 117 static int hwdep_unlock(struct snd_motu *motu) 118 { 119 int err; 120 121 spin_lock_irq(&motu->lock); 122 123 if (motu->dev_lock_count == -1) { 124 motu->dev_lock_count = 0; 125 err = 0; 126 } else { 127 err = -EBADFD; 128 } 129 130 spin_unlock_irq(&motu->lock); 131 132 return err; 133 } 134 135 static int hwdep_release(struct snd_hwdep *hwdep, struct file *file) 136 { 137 struct snd_motu *motu = hwdep->private_data; 138 139 spin_lock_irq(&motu->lock); 140 if (motu->dev_lock_count == -1) 141 motu->dev_lock_count = 0; 142 spin_unlock_irq(&motu->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_motu *motu = hwdep->private_data; 151 152 switch (cmd) { 153 case SNDRV_FIREWIRE_IOCTL_GET_INFO: 154 return hwdep_get_info(motu, (void __user *)arg); 155 case SNDRV_FIREWIRE_IOCTL_LOCK: 156 return hwdep_lock(motu); 157 case SNDRV_FIREWIRE_IOCTL_UNLOCK: 158 return hwdep_unlock(motu); 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_motu_create_hwdep_device(struct snd_motu *motu) 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(motu->card, motu->card->driver, 0, &hwdep); 188 if (err < 0) 189 return err; 190 191 strcpy(hwdep->name, "MOTU"); 192 hwdep->iface = SNDRV_HWDEP_IFACE_FW_MOTU; 193 hwdep->ops = ops; 194 hwdep->private_data = motu; 195 hwdep->exclusive = true; 196 197 return 0; 198 } 199