1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * oxfw_hwdep.c - a part of driver for OXFW970/971 based devices 4 * 5 * Copyright (c) 2014 Takashi Sakamoto 6 */ 7 8 /* 9 * This codes give three functionality. 10 * 11 * 1.get firewire node information 12 * 2.get notification about starting/stopping stream 13 * 3.lock/unlock stream 14 */ 15 16 #include "oxfw.h" 17 18 static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, 19 loff_t *offset) 20 { 21 struct snd_oxfw *oxfw = hwdep->private_data; 22 DEFINE_WAIT(wait); 23 union snd_firewire_event event; 24 25 spin_lock_irq(&oxfw->lock); 26 27 while (!oxfw->dev_lock_changed) { 28 prepare_to_wait(&oxfw->hwdep_wait, &wait, TASK_INTERRUPTIBLE); 29 spin_unlock_irq(&oxfw->lock); 30 schedule(); 31 finish_wait(&oxfw->hwdep_wait, &wait); 32 if (signal_pending(current)) 33 return -ERESTARTSYS; 34 spin_lock_irq(&oxfw->lock); 35 } 36 37 memset(&event, 0, sizeof(event)); 38 event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; 39 event.lock_status.status = (oxfw->dev_lock_count > 0); 40 oxfw->dev_lock_changed = false; 41 42 count = min_t(long, count, sizeof(event.lock_status)); 43 44 spin_unlock_irq(&oxfw->lock); 45 46 if (copy_to_user(buf, &event, count)) 47 return -EFAULT; 48 49 return count; 50 } 51 52 static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file, 53 poll_table *wait) 54 { 55 struct snd_oxfw *oxfw = hwdep->private_data; 56 57 poll_wait(file, &oxfw->hwdep_wait, wait); 58 59 guard(spinlock_irq)(&oxfw->lock); 60 if (oxfw->dev_lock_changed) 61 return EPOLLIN | EPOLLRDNORM; 62 else 63 return 0; 64 } 65 66 static int hwdep_get_info(struct snd_oxfw *oxfw, void __user *arg) 67 { 68 struct fw_device *dev = fw_parent_device(oxfw->unit); 69 struct snd_firewire_get_info info; 70 71 memset(&info, 0, sizeof(info)); 72 info.type = SNDRV_FIREWIRE_TYPE_OXFW; 73 info.card = dev->card->index; 74 *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); 75 *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); 76 strscpy(info.device_name, dev_name(&dev->device), 77 sizeof(info.device_name)); 78 79 if (copy_to_user(arg, &info, sizeof(info))) 80 return -EFAULT; 81 82 return 0; 83 } 84 85 static int hwdep_lock(struct snd_oxfw *oxfw) 86 { 87 guard(spinlock_irq)(&oxfw->lock); 88 89 if (oxfw->dev_lock_count == 0) { 90 oxfw->dev_lock_count = -1; 91 return 0; 92 } else { 93 return -EBUSY; 94 } 95 } 96 97 static int hwdep_unlock(struct snd_oxfw *oxfw) 98 { 99 guard(spinlock_irq)(&oxfw->lock); 100 101 if (oxfw->dev_lock_count == -1) { 102 oxfw->dev_lock_count = 0; 103 return 0; 104 } else { 105 return -EBADFD; 106 } 107 } 108 109 static int hwdep_release(struct snd_hwdep *hwdep, struct file *file) 110 { 111 struct snd_oxfw *oxfw = hwdep->private_data; 112 113 guard(spinlock_irq)(&oxfw->lock); 114 if (oxfw->dev_lock_count == -1) 115 oxfw->dev_lock_count = 0; 116 117 return 0; 118 } 119 120 static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, 121 unsigned int cmd, unsigned long arg) 122 { 123 struct snd_oxfw *oxfw = hwdep->private_data; 124 125 switch (cmd) { 126 case SNDRV_FIREWIRE_IOCTL_GET_INFO: 127 return hwdep_get_info(oxfw, (void __user *)arg); 128 case SNDRV_FIREWIRE_IOCTL_LOCK: 129 return hwdep_lock(oxfw); 130 case SNDRV_FIREWIRE_IOCTL_UNLOCK: 131 return hwdep_unlock(oxfw); 132 default: 133 return -ENOIOCTLCMD; 134 } 135 } 136 137 #ifdef CONFIG_COMPAT 138 static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, 139 unsigned int cmd, unsigned long arg) 140 { 141 return hwdep_ioctl(hwdep, file, cmd, 142 (unsigned long)compat_ptr(arg)); 143 } 144 #else 145 #define hwdep_compat_ioctl NULL 146 #endif 147 148 int snd_oxfw_create_hwdep(struct snd_oxfw *oxfw) 149 { 150 static const struct snd_hwdep_ops hwdep_ops = { 151 .read = hwdep_read, 152 .release = hwdep_release, 153 .poll = hwdep_poll, 154 .ioctl = hwdep_ioctl, 155 .ioctl_compat = hwdep_compat_ioctl, 156 }; 157 struct snd_hwdep *hwdep; 158 int err; 159 160 err = snd_hwdep_new(oxfw->card, oxfw->card->driver, 0, &hwdep); 161 if (err < 0) 162 goto end; 163 strscpy(hwdep->name, oxfw->card->driver); 164 hwdep->iface = SNDRV_HWDEP_IFACE_FW_OXFW; 165 hwdep->ops = hwdep_ops; 166 hwdep->private_data = oxfw; 167 hwdep->exclusive = true; 168 end: 169 return err; 170 } 171