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 20static 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 63static 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 81static 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 100static 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 118static 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 136static 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 148static 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 166static 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 176static const struct snd_hwdep_ops hwdep_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 184int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x) 185{ 186 struct snd_hwdep *hwdep; 187 int err; 188 189 err = snd_hwdep_new(dg00x->card, "Digi00x", 0, &hwdep); 190 if (err < 0) 191 return err; 192 193 strcpy(hwdep->name, "Digi00x"); 194 hwdep->iface = SNDRV_HWDEP_IFACE_FW_DIGI00X; 195 hwdep->ops = hwdep_ops; 196 hwdep->private_data = dg00x; 197 hwdep->exclusive = true; 198 199 return err; 200} 201