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