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