Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ALSA: fireface: add hwdep interface

This commit adds hwdep interface so as the other drivers for audio and
music units on IEEE 1394 have.

This interface is designed for mixer/control applications. By using this
interface, an application can get information about firewire node, can
lock/unlock kernel streaming and can get notification at starting/stopping
kernel streaming.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>

authored by

Takashi Sakamoto and committed by
Takashi Iwai
f656edd5 4b316436

+271 -7
+2 -1
include/uapi/sound/asound.h
··· 108 108 SNDRV_HWDEP_IFACE_FW_TASCAM, /* TASCAM FireWire series */ 109 109 SNDRV_HWDEP_IFACE_LINE6, /* Line6 USB processors */ 110 110 SNDRV_HWDEP_IFACE_FW_MOTU, /* MOTU FireWire series */ 111 + SNDRV_HWDEP_IFACE_FW_FIREFACE, /* RME Fireface series */ 111 112 112 113 /* Don't forget to change the following: */ 113 - SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_MOTU 114 + SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_FIREFACE 114 115 }; 115 116 116 117 struct snd_hwdep_info {
+1 -1
include/uapi/sound/firewire.h
··· 73 73 #define SNDRV_FIREWIRE_TYPE_DIGI00X 5 74 74 #define SNDRV_FIREWIRE_TYPE_TASCAM 6 75 75 #define SNDRV_FIREWIRE_TYPE_MOTU 7 76 - /* RME... */ 76 + #define SNDRV_FIREWIRE_TYPE_FIREFACE 8 77 77 78 78 struct snd_firewire_get_info { 79 79 unsigned int type; /* SNDRV_FIREWIRE_TYPE_xxx */
+1
sound/firewire/Kconfig
··· 155 155 config SND_FIREFACE 156 156 tristate "RME Fireface series support" 157 157 select SND_FIREWIRE_LIB 158 + select SND_HWDEP 158 159 help 159 160 Say Y here to include support for RME fireface series. 160 161
+1 -1
sound/firewire/fireface/Makefile
··· 1 1 snd-fireface-objs := ff.o ff-transaction.o ff-midi.o ff-proc.o amdtp-ff.o \ 2 - ff-stream.o ff-pcm.o 2 + ff-stream.o ff-pcm.o ff-hwdep.o 3 3 obj-$(CONFIG_SND_FIREFACE) += snd-fireface.o
+191
sound/firewire/fireface/ff-hwdep.c
··· 1 + /* 2 + * ff-hwdep.c - a part of driver for RME Fireface series 3 + * 4 + * Copyright (c) 2015-2017 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 "ff.h" 18 + 19 + static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, 20 + loff_t *offset) 21 + { 22 + struct snd_ff *ff = hwdep->private_data; 23 + DEFINE_WAIT(wait); 24 + union snd_firewire_event event; 25 + 26 + spin_lock_irq(&ff->lock); 27 + 28 + while (!ff->dev_lock_changed) { 29 + prepare_to_wait(&ff->hwdep_wait, &wait, TASK_INTERRUPTIBLE); 30 + spin_unlock_irq(&ff->lock); 31 + schedule(); 32 + finish_wait(&ff->hwdep_wait, &wait); 33 + if (signal_pending(current)) 34 + return -ERESTARTSYS; 35 + spin_lock_irq(&ff->lock); 36 + } 37 + 38 + memset(&event, 0, sizeof(event)); 39 + if (ff->dev_lock_changed) { 40 + event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; 41 + event.lock_status.status = (ff->dev_lock_count > 0); 42 + ff->dev_lock_changed = false; 43 + 44 + count = min_t(long, count, sizeof(event.lock_status)); 45 + } 46 + 47 + spin_unlock_irq(&ff->lock); 48 + 49 + if (copy_to_user(buf, &event, count)) 50 + return -EFAULT; 51 + 52 + return count; 53 + } 54 + 55 + static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file, 56 + poll_table *wait) 57 + { 58 + struct snd_ff *ff = hwdep->private_data; 59 + unsigned int events; 60 + 61 + poll_wait(file, &ff->hwdep_wait, wait); 62 + 63 + spin_lock_irq(&ff->lock); 64 + if (ff->dev_lock_changed) 65 + events = POLLIN | POLLRDNORM; 66 + else 67 + events = 0; 68 + spin_unlock_irq(&ff->lock); 69 + 70 + return events; 71 + } 72 + 73 + static int hwdep_get_info(struct snd_ff *ff, void __user *arg) 74 + { 75 + struct fw_device *dev = fw_parent_device(ff->unit); 76 + struct snd_firewire_get_info info; 77 + 78 + memset(&info, 0, sizeof(info)); 79 + info.type = SNDRV_FIREWIRE_TYPE_FIREFACE; 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_ff *ff) 93 + { 94 + int err; 95 + 96 + spin_lock_irq(&ff->lock); 97 + 98 + if (ff->dev_lock_count == 0) { 99 + ff->dev_lock_count = -1; 100 + err = 0; 101 + } else { 102 + err = -EBUSY; 103 + } 104 + 105 + spin_unlock_irq(&ff->lock); 106 + 107 + return err; 108 + } 109 + 110 + static int hwdep_unlock(struct snd_ff *ff) 111 + { 112 + int err; 113 + 114 + spin_lock_irq(&ff->lock); 115 + 116 + if (ff->dev_lock_count == -1) { 117 + ff->dev_lock_count = 0; 118 + err = 0; 119 + } else { 120 + err = -EBADFD; 121 + } 122 + 123 + spin_unlock_irq(&ff->lock); 124 + 125 + return err; 126 + } 127 + 128 + static int hwdep_release(struct snd_hwdep *hwdep, struct file *file) 129 + { 130 + struct snd_ff *ff = hwdep->private_data; 131 + 132 + spin_lock_irq(&ff->lock); 133 + if (ff->dev_lock_count == -1) 134 + ff->dev_lock_count = 0; 135 + spin_unlock_irq(&ff->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_ff *ff = hwdep->private_data; 144 + 145 + switch (cmd) { 146 + case SNDRV_FIREWIRE_IOCTL_GET_INFO: 147 + return hwdep_get_info(ff, (void __user *)arg); 148 + case SNDRV_FIREWIRE_IOCTL_LOCK: 149 + return hwdep_lock(ff); 150 + case SNDRV_FIREWIRE_IOCTL_UNLOCK: 151 + return hwdep_unlock(ff); 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_ff_create_hwdep_devices(struct snd_ff *ff) 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(ff->card, ff->card->driver, 0, &hwdep); 181 + if (err < 0) 182 + return err; 183 + 184 + strcpy(hwdep->name, ff->card->driver); 185 + hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREFACE; 186 + hwdep->ops = hwdep_ops; 187 + hwdep->private_data = ff; 188 + hwdep->exclusive = true; 189 + 190 + return 0; 191 + }
+18 -4
sound/firewire/fireface/ff-pcm.c
··· 154 154 enum snd_ff_clock_src src; 155 155 int i, err; 156 156 157 - err = pcm_init_hw_params(ff, substream); 157 + err = snd_ff_stream_lock_try(ff); 158 158 if (err < 0) 159 159 return err; 160 160 161 - err = ff->spec->protocol->get_clock(ff, &rate, &src); 162 - if (err < 0) 161 + err = pcm_init_hw_params(ff, substream); 162 + if (err < 0) { 163 + snd_ff_stream_lock_release(ff); 163 164 return err; 165 + } 166 + 167 + err = ff->spec->protocol->get_clock(ff, &rate, &src); 168 + if (err < 0) { 169 + snd_ff_stream_lock_release(ff); 170 + return err; 171 + } 164 172 165 173 if (src != SND_FF_CLOCK_SRC_INTERNAL) { 166 174 for (i = 0; i < CIP_SFC_COUNT; ++i) { ··· 179 171 * The unit is configured at sampling frequency which packet 180 172 * streaming engine can't support. 181 173 */ 182 - if (i >= CIP_SFC_COUNT) 174 + if (i >= CIP_SFC_COUNT) { 175 + snd_ff_stream_lock_release(ff); 183 176 return -EIO; 177 + } 184 178 185 179 substream->runtime->hw.rate_min = rate; 186 180 substream->runtime->hw.rate_max = rate; ··· 202 192 203 193 static int pcm_close(struct snd_pcm_substream *substream) 204 194 { 195 + struct snd_ff *ff = substream->private_data; 196 + 197 + snd_ff_stream_lock_release(ff); 198 + 205 199 return 0; 206 200 } 207 201
+39
sound/firewire/fireface/ff-stream.c
··· 241 241 fw_iso_resources_update(&ff->tx_resources); 242 242 fw_iso_resources_update(&ff->rx_resources); 243 243 } 244 + 245 + void snd_ff_stream_lock_changed(struct snd_ff *ff) 246 + { 247 + ff->dev_lock_changed = true; 248 + wake_up(&ff->hwdep_wait); 249 + } 250 + 251 + int snd_ff_stream_lock_try(struct snd_ff *ff) 252 + { 253 + int err; 254 + 255 + spin_lock_irq(&ff->lock); 256 + 257 + /* user land lock this */ 258 + if (ff->dev_lock_count < 0) { 259 + err = -EBUSY; 260 + goto end; 261 + } 262 + 263 + /* this is the first time */ 264 + if (ff->dev_lock_count++ == 0) 265 + snd_ff_stream_lock_changed(ff); 266 + err = 0; 267 + end: 268 + spin_unlock_irq(&ff->lock); 269 + return err; 270 + } 271 + 272 + void snd_ff_stream_lock_release(struct snd_ff *ff) 273 + { 274 + spin_lock_irq(&ff->lock); 275 + 276 + if (WARN_ON(ff->dev_lock_count <= 0)) 277 + goto end; 278 + if (--ff->dev_lock_count == 0) 279 + snd_ff_stream_lock_changed(ff); 280 + end: 281 + spin_unlock_irq(&ff->lock); 282 + }
+5
sound/firewire/fireface/ff.c
··· 76 76 if (err < 0) 77 77 goto error; 78 78 79 + err = snd_ff_create_hwdep_devices(ff); 80 + if (err < 0) 81 + goto error; 82 + 79 83 err = snd_card_register(ff->card); 80 84 if (err < 0) 81 85 goto error; ··· 112 108 113 109 mutex_init(&ff->mutex); 114 110 spin_lock_init(&ff->lock); 111 + init_waitqueue_head(&ff->hwdep_wait); 115 112 116 113 ff->spec = (const struct snd_ff_spec *)entry->driver_data; 117 114
+13
sound/firewire/fireface/ff.h
··· 17 17 #include <linux/mutex.h> 18 18 #include <linux/slab.h> 19 19 #include <linux/compat.h> 20 + #include <linux/sched/signal.h> 20 21 21 22 #include <sound/core.h> 22 23 #include <sound/info.h> 23 24 #include <sound/rawmidi.h> 24 25 #include <sound/pcm.h> 25 26 #include <sound/pcm_params.h> 27 + #include <sound/hwdep.h> 28 + #include <sound/firewire.h> 26 29 27 30 #include "../lib.h" 28 31 #include "../amdtp-stream.h" ··· 80 77 struct amdtp_stream rx_stream; 81 78 struct fw_iso_resources tx_resources; 82 79 struct fw_iso_resources rx_resources; 80 + 81 + int dev_lock_count; 82 + bool dev_lock_changed; 83 + wait_queue_head_t hwdep_wait; 83 84 }; 84 85 85 86 enum snd_ff_clock_src { ··· 129 122 void snd_ff_stream_stop_duplex(struct snd_ff *ff); 130 123 void snd_ff_stream_update_duplex(struct snd_ff *ff); 131 124 125 + void snd_ff_stream_lock_changed(struct snd_ff *ff); 126 + int snd_ff_stream_lock_try(struct snd_ff *ff); 127 + void snd_ff_stream_lock_release(struct snd_ff *ff); 128 + 132 129 void snd_ff_proc_init(struct snd_ff *ff); 133 130 134 131 int snd_ff_create_midi_devices(struct snd_ff *ff); 135 132 136 133 int snd_ff_create_pcm_devices(struct snd_ff *ff); 134 + 135 + int snd_ff_create_hwdep_devices(struct snd_ff *ff); 137 136 138 137 #endif