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