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