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