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