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 count = min_t(long, count, sizeof(event.lock_status)); 40 if (bebob->dev_lock_changed) { 41 event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; 42 event.lock_status.status = (bebob->dev_lock_count > 0); 43 bebob->dev_lock_changed = false; 44 } 45 46 spin_unlock_irq(&bebob->lock); 47 48 if (copy_to_user(buf, &event, count)) 49 return -EFAULT; 50 51 return count; 52 } 53 54 static __poll_t 55 hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait) 56 { 57 struct snd_bebob *bebob = hwdep->private_data; 58 __poll_t events; 59 60 poll_wait(file, &bebob->hwdep_wait, wait); 61 62 spin_lock_irq(&bebob->lock); 63 if (bebob->dev_lock_changed) 64 events = EPOLLIN | EPOLLRDNORM; 65 else 66 events = 0; 67 spin_unlock_irq(&bebob->lock); 68 69 return events; 70 } 71 72 static int 73 hwdep_get_info(struct snd_bebob *bebob, void __user *arg) 74 { 75 struct fw_device *dev = fw_parent_device(bebob->unit); 76 struct snd_firewire_get_info info; 77 78 memset(&info, 0, sizeof(info)); 79 info.type = SNDRV_FIREWIRE_TYPE_BEBOB; 80 info.card = dev->card->index; 81 *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); 82 *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); 83 strlcpy(info.device_name, dev_name(&dev->device), 84 sizeof(info.device_name)); 85 86 if (copy_to_user(arg, &info, sizeof(info))) 87 return -EFAULT; 88 89 return 0; 90 } 91 92 static int 93 hwdep_lock(struct snd_bebob *bebob) 94 { 95 int err; 96 97 spin_lock_irq(&bebob->lock); 98 99 if (bebob->dev_lock_count == 0) { 100 bebob->dev_lock_count = -1; 101 err = 0; 102 } else { 103 err = -EBUSY; 104 } 105 106 spin_unlock_irq(&bebob->lock); 107 108 return err; 109 } 110 111 static int 112 hwdep_unlock(struct snd_bebob *bebob) 113 { 114 int err; 115 116 spin_lock_irq(&bebob->lock); 117 118 if (bebob->dev_lock_count == -1) { 119 bebob->dev_lock_count = 0; 120 err = 0; 121 } else { 122 err = -EBADFD; 123 } 124 125 spin_unlock_irq(&bebob->lock); 126 127 return err; 128 } 129 130 static int 131 hwdep_release(struct snd_hwdep *hwdep, struct file *file) 132 { 133 struct snd_bebob *bebob = hwdep->private_data; 134 135 spin_lock_irq(&bebob->lock); 136 if (bebob->dev_lock_count == -1) 137 bebob->dev_lock_count = 0; 138 spin_unlock_irq(&bebob->lock); 139 140 return 0; 141 } 142 143 static int 144 hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, 145 unsigned int cmd, unsigned long arg) 146 { 147 struct snd_bebob *bebob = hwdep->private_data; 148 149 switch (cmd) { 150 case SNDRV_FIREWIRE_IOCTL_GET_INFO: 151 return hwdep_get_info(bebob, (void __user *)arg); 152 case SNDRV_FIREWIRE_IOCTL_LOCK: 153 return hwdep_lock(bebob); 154 case SNDRV_FIREWIRE_IOCTL_UNLOCK: 155 return hwdep_unlock(bebob); 156 default: 157 return -ENOIOCTLCMD; 158 } 159 } 160 161 #ifdef CONFIG_COMPAT 162 static int 163 hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, 164 unsigned int cmd, unsigned long arg) 165 { 166 return hwdep_ioctl(hwdep, file, cmd, 167 (unsigned long)compat_ptr(arg)); 168 } 169 #else 170 #define hwdep_compat_ioctl NULL 171 #endif 172 173 int snd_bebob_create_hwdep_device(struct snd_bebob *bebob) 174 { 175 static const struct snd_hwdep_ops ops = { 176 .read = hwdep_read, 177 .release = hwdep_release, 178 .poll = hwdep_poll, 179 .ioctl = hwdep_ioctl, 180 .ioctl_compat = hwdep_compat_ioctl, 181 }; 182 struct snd_hwdep *hwdep; 183 int err; 184 185 err = snd_hwdep_new(bebob->card, "BeBoB", 0, &hwdep); 186 if (err < 0) 187 goto end; 188 strcpy(hwdep->name, "BeBoB"); 189 hwdep->iface = SNDRV_HWDEP_IFACE_FW_BEBOB; 190 hwdep->ops = ops; 191 hwdep->private_data = bebob; 192 hwdep->exclusive = true; 193 end: 194 return err; 195 } 196 197