1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * fireworks_hwdep.c - a part of driver for Fireworks based devices 4 * 5 * Copyright (c) 2013-2014 Takashi Sakamoto 6 */ 7 8 /* 9 * This codes have five functionalities. 10 * 11 * 1.get information about firewire node 12 * 2.get notification about starting/stopping stream 13 * 3.lock/unlock streaming 14 * 4.transmit command of EFW transaction 15 * 5.receive response of EFW transaction 16 * 17 */ 18 19 #include "fireworks.h" 20 21 static long 22 hwdep_read_resp_buf(struct snd_efw *efw, char __user *buf, long remained, 23 loff_t *offset) 24 { 25 unsigned int length, till_end, type; 26 struct snd_efw_transaction *t; 27 u8 *pull_ptr; 28 long count = 0; 29 30 if (remained < sizeof(type) + sizeof(struct snd_efw_transaction)) 31 return -ENOSPC; 32 33 /* data type is SNDRV_FIREWIRE_EVENT_EFW_RESPONSE */ 34 type = SNDRV_FIREWIRE_EVENT_EFW_RESPONSE; 35 if (copy_to_user(buf, &type, sizeof(type))) 36 return -EFAULT; 37 remained -= sizeof(type); 38 buf += sizeof(type); 39 40 /* write into buffer as many responses as possible */ 41 spin_lock_irq(&efw->lock); 42 43 /* 44 * When another task reaches here during this task's access to user 45 * space, it picks up current position in buffer and can read the same 46 * series of responses. 47 */ 48 pull_ptr = efw->pull_ptr; 49 50 while (efw->push_ptr != pull_ptr) { 51 t = (struct snd_efw_transaction *)(pull_ptr); 52 length = be32_to_cpu(t->length) * sizeof(__be32); 53 54 /* confirm enough space for this response */ 55 if (remained < length) 56 break; 57 58 /* copy from ring buffer to user buffer */ 59 while (length > 0) { 60 till_end = snd_efw_resp_buf_size - 61 (unsigned int)(pull_ptr - efw->resp_buf); 62 till_end = min_t(unsigned int, length, till_end); 63 64 spin_unlock_irq(&efw->lock); 65 66 if (copy_to_user(buf, pull_ptr, till_end)) 67 return -EFAULT; 68 69 spin_lock_irq(&efw->lock); 70 71 pull_ptr += till_end; 72 if (pull_ptr >= efw->resp_buf + snd_efw_resp_buf_size) 73 pull_ptr -= snd_efw_resp_buf_size; 74 75 length -= till_end; 76 buf += till_end; 77 count += till_end; 78 remained -= till_end; 79 } 80 } 81 82 /* 83 * All of tasks can read from the buffer nearly simultaneously, but the 84 * last position for each task is different depending on the length of 85 * given buffer. Here, for simplicity, a position of buffer is set by 86 * the latest task. It's better for a listening application to allow one 87 * thread to read from the buffer. Unless, each task can read different 88 * sequence of responses depending on variation of buffer length. 89 */ 90 efw->pull_ptr = pull_ptr; 91 92 spin_unlock_irq(&efw->lock); 93 94 return count; 95 } 96 97 static long 98 hwdep_read_locked(struct snd_efw *efw, char __user *buf, long count, 99 loff_t *offset) 100 { 101 union snd_firewire_event event = { 102 .lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS, 103 }; 104 105 spin_lock_irq(&efw->lock); 106 107 event.lock_status.status = (efw->dev_lock_count > 0); 108 efw->dev_lock_changed = false; 109 110 spin_unlock_irq(&efw->lock); 111 112 count = min_t(long, count, sizeof(event.lock_status)); 113 114 if (copy_to_user(buf, &event, count)) 115 return -EFAULT; 116 117 return count; 118 } 119 120 static long 121 hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, 122 loff_t *offset) 123 { 124 struct snd_efw *efw = hwdep->private_data; 125 DEFINE_WAIT(wait); 126 bool dev_lock_changed; 127 bool queued; 128 129 spin_lock_irq(&efw->lock); 130 131 dev_lock_changed = efw->dev_lock_changed; 132 queued = efw->push_ptr != efw->pull_ptr; 133 134 while (!dev_lock_changed && !queued) { 135 prepare_to_wait(&efw->hwdep_wait, &wait, TASK_INTERRUPTIBLE); 136 spin_unlock_irq(&efw->lock); 137 schedule(); 138 finish_wait(&efw->hwdep_wait, &wait); 139 if (signal_pending(current)) 140 return -ERESTARTSYS; 141 spin_lock_irq(&efw->lock); 142 dev_lock_changed = efw->dev_lock_changed; 143 queued = efw->push_ptr != efw->pull_ptr; 144 } 145 146 spin_unlock_irq(&efw->lock); 147 148 if (dev_lock_changed) 149 count = hwdep_read_locked(efw, buf, count, offset); 150 else if (queued) 151 count = hwdep_read_resp_buf(efw, buf, count, offset); 152 153 return count; 154 } 155 156 static long 157 hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count, 158 loff_t *offset) 159 { 160 struct snd_efw *efw = hwdep->private_data; 161 u32 seqnum; 162 u8 *buf; 163 164 if (count < sizeof(struct snd_efw_transaction) || 165 SND_EFW_RESPONSE_MAXIMUM_BYTES < count) 166 return -EINVAL; 167 168 buf = memdup_user(data, count); 169 if (IS_ERR(buf)) 170 return PTR_ERR(buf); 171 172 /* check seqnum is not for kernel-land */ 173 seqnum = be32_to_cpu(((struct snd_efw_transaction *)buf)->seqnum); 174 if (seqnum > SND_EFW_TRANSACTION_USER_SEQNUM_MAX) { 175 count = -EINVAL; 176 goto end; 177 } 178 179 if (snd_efw_transaction_cmd(efw->unit, buf, count) < 0) 180 count = -EIO; 181 end: 182 kfree(buf); 183 return count; 184 } 185 186 static __poll_t 187 hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait) 188 { 189 struct snd_efw *efw = hwdep->private_data; 190 __poll_t events; 191 192 poll_wait(file, &efw->hwdep_wait, wait); 193 194 spin_lock_irq(&efw->lock); 195 if (efw->dev_lock_changed || efw->pull_ptr != efw->push_ptr) 196 events = EPOLLIN | EPOLLRDNORM; 197 else 198 events = 0; 199 spin_unlock_irq(&efw->lock); 200 201 return events | EPOLLOUT; 202 } 203 204 static int 205 hwdep_get_info(struct snd_efw *efw, void __user *arg) 206 { 207 struct fw_device *dev = fw_parent_device(efw->unit); 208 struct snd_firewire_get_info info; 209 210 memset(&info, 0, sizeof(info)); 211 info.type = SNDRV_FIREWIRE_TYPE_FIREWORKS; 212 info.card = dev->card->index; 213 *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); 214 *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); 215 strscpy(info.device_name, dev_name(&dev->device), 216 sizeof(info.device_name)); 217 218 if (copy_to_user(arg, &info, sizeof(info))) 219 return -EFAULT; 220 221 return 0; 222 } 223 224 static int 225 hwdep_lock(struct snd_efw *efw) 226 { 227 int err; 228 229 spin_lock_irq(&efw->lock); 230 231 if (efw->dev_lock_count == 0) { 232 efw->dev_lock_count = -1; 233 err = 0; 234 } else { 235 err = -EBUSY; 236 } 237 238 spin_unlock_irq(&efw->lock); 239 240 return err; 241 } 242 243 static int 244 hwdep_unlock(struct snd_efw *efw) 245 { 246 int err; 247 248 spin_lock_irq(&efw->lock); 249 250 if (efw->dev_lock_count == -1) { 251 efw->dev_lock_count = 0; 252 err = 0; 253 } else { 254 err = -EBADFD; 255 } 256 257 spin_unlock_irq(&efw->lock); 258 259 return err; 260 } 261 262 static int 263 hwdep_release(struct snd_hwdep *hwdep, struct file *file) 264 { 265 struct snd_efw *efw = hwdep->private_data; 266 267 spin_lock_irq(&efw->lock); 268 if (efw->dev_lock_count == -1) 269 efw->dev_lock_count = 0; 270 spin_unlock_irq(&efw->lock); 271 272 return 0; 273 } 274 275 static int 276 hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, 277 unsigned int cmd, unsigned long arg) 278 { 279 struct snd_efw *efw = hwdep->private_data; 280 281 switch (cmd) { 282 case SNDRV_FIREWIRE_IOCTL_GET_INFO: 283 return hwdep_get_info(efw, (void __user *)arg); 284 case SNDRV_FIREWIRE_IOCTL_LOCK: 285 return hwdep_lock(efw); 286 case SNDRV_FIREWIRE_IOCTL_UNLOCK: 287 return hwdep_unlock(efw); 288 default: 289 return -ENOIOCTLCMD; 290 } 291 } 292 293 #ifdef CONFIG_COMPAT 294 static int 295 hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, 296 unsigned int cmd, unsigned long arg) 297 { 298 return hwdep_ioctl(hwdep, file, cmd, 299 (unsigned long)compat_ptr(arg)); 300 } 301 #else 302 #define hwdep_compat_ioctl NULL 303 #endif 304 305 int snd_efw_create_hwdep_device(struct snd_efw *efw) 306 { 307 static const struct snd_hwdep_ops ops = { 308 .read = hwdep_read, 309 .write = hwdep_write, 310 .release = hwdep_release, 311 .poll = hwdep_poll, 312 .ioctl = hwdep_ioctl, 313 .ioctl_compat = hwdep_compat_ioctl, 314 }; 315 struct snd_hwdep *hwdep; 316 int err; 317 318 err = snd_hwdep_new(efw->card, "Fireworks", 0, &hwdep); 319 if (err < 0) 320 goto end; 321 strcpy(hwdep->name, "Fireworks"); 322 hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREWORKS; 323 hwdep->ops = ops; 324 hwdep->private_data = efw; 325 hwdep->exclusive = true; 326 end: 327 return err; 328 } 329 330