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