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

ALSA: oxfw: Add hwdep interface

This interface is designed for mixer/control application. 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>
Acked-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>

authored by

Takashi Sakamoto and committed by
Takashi Iwai
8985f4ac 05588d34

+280 -4
+2 -1
include/uapi/sound/asound.h
··· 96 96 SNDRV_HWDEP_IFACE_FW_DICE, /* TC DICE FireWire device */ 97 97 SNDRV_HWDEP_IFACE_FW_FIREWORKS, /* Echo Audio Fireworks based device */ 98 98 SNDRV_HWDEP_IFACE_FW_BEBOB, /* BridgeCo BeBoB based device */ 99 + SNDRV_HWDEP_IFACE_FW_OXFW, /* Oxford OXFW970/971 based device */ 99 100 100 101 /* Don't forget to change the following: */ 101 - SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_BEBOB 102 + SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_OXFW 102 103 }; 103 104 104 105 struct snd_hwdep_info {
+2 -1
include/uapi/sound/firewire.h
··· 55 55 #define SNDRV_FIREWIRE_TYPE_DICE 1 56 56 #define SNDRV_FIREWIRE_TYPE_FIREWORKS 2 57 57 #define SNDRV_FIREWIRE_TYPE_BEBOB 3 58 - /* AV/C, RME, MOTU, ... */ 58 + #define SNDRV_FIREWIRE_TYPE_OXFW 4 59 + /* RME, MOTU, ... */ 59 60 60 61 struct snd_firewire_get_info { 61 62 unsigned int type; /* SNDRV_FIREWIRE_TYPE_xxx */
+1
sound/firewire/Kconfig
··· 26 26 config SND_OXFW 27 27 tristate "Oxford Semiconductor FW970/971 chipset support" 28 28 select SND_FIREWIRE_LIB 29 + select SND_HWDEP 29 30 help 30 31 Say Y here to include support for FireWire devices based on 31 32 Oxford Semiconductor FW970/971 chipset.
+1 -1
sound/firewire/oxfw/Makefile
··· 1 1 snd-oxfw-objs := oxfw-command.o oxfw-stream.o oxfw-control.o oxfw-pcm.o \ 2 - oxfw-proc.o oxfw-midi.o oxfw.o 2 + oxfw-proc.o oxfw-midi.o oxfw-hwdep.o oxfw.o 3 3 obj-m += snd-oxfw.o
+190
sound/firewire/oxfw/oxfw-hwdep.c
··· 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 unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file, 56 + poll_table *wait) 57 + { 58 + struct snd_oxfw *oxfw = hwdep->private_data; 59 + unsigned int 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 = POLLIN | POLLRDNORM; 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 + }
+16
sound/firewire/oxfw/oxfw-midi.c
··· 13 13 struct snd_oxfw *oxfw = substream->rmidi->private_data; 14 14 int err; 15 15 16 + err = snd_oxfw_stream_lock_try(oxfw); 17 + if (err < 0) 18 + return err; 19 + 16 20 mutex_lock(&oxfw->mutex); 17 21 18 22 oxfw->capture_substreams++; 19 23 err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->tx_stream, 0, 0); 20 24 21 25 mutex_unlock(&oxfw->mutex); 26 + 27 + if (err < 0) 28 + snd_oxfw_stream_lock_release(oxfw); 22 29 23 30 return err; 24 31 } ··· 35 28 struct snd_oxfw *oxfw = substream->rmidi->private_data; 36 29 int err; 37 30 31 + err = snd_oxfw_stream_lock_try(oxfw); 32 + if (err < 0) 33 + return err; 34 + 38 35 mutex_lock(&oxfw->mutex); 39 36 40 37 oxfw->playback_substreams++; 41 38 err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->rx_stream, 0, 0); 42 39 43 40 mutex_unlock(&oxfw->mutex); 41 + 42 + if (err < 0) 43 + snd_oxfw_stream_lock_release(oxfw); 44 44 45 45 return err; 46 46 } ··· 63 49 64 50 mutex_unlock(&oxfw->mutex); 65 51 52 + snd_oxfw_stream_lock_release(oxfw); 66 53 return 0; 67 54 } 68 55 ··· 78 63 79 64 mutex_unlock(&oxfw->mutex); 80 65 66 + snd_oxfw_stream_lock_release(oxfw); 81 67 return 0; 82 68 } 83 69
+11 -1
sound/firewire/oxfw/oxfw-pcm.c
··· 192 192 struct snd_oxfw *oxfw = substream->private_data; 193 193 int err; 194 194 195 - err = init_hw_params(oxfw, substream); 195 + err = snd_oxfw_stream_lock_try(oxfw); 196 196 if (err < 0) 197 197 goto end; 198 + 199 + err = init_hw_params(oxfw, substream); 200 + if (err < 0) 201 + goto err_locked; 198 202 199 203 /* 200 204 * When any PCM streams are already running, the available sampling ··· 214 210 snd_pcm_set_sync(substream); 215 211 end: 216 212 return err; 213 + err_locked: 214 + snd_oxfw_stream_lock_release(oxfw); 215 + return err; 217 216 } 218 217 219 218 static int pcm_close(struct snd_pcm_substream *substream) 220 219 { 220 + struct snd_oxfw *oxfw = substream->private_data; 221 + 222 + snd_oxfw_stream_lock_release(oxfw); 221 223 return 0; 222 224 } 223 225
+39
sound/firewire/oxfw/oxfw-stream.c
··· 644 644 end: 645 645 return err; 646 646 } 647 + 648 + void snd_oxfw_stream_lock_changed(struct snd_oxfw *oxfw) 649 + { 650 + oxfw->dev_lock_changed = true; 651 + wake_up(&oxfw->hwdep_wait); 652 + } 653 + 654 + int snd_oxfw_stream_lock_try(struct snd_oxfw *oxfw) 655 + { 656 + int err; 657 + 658 + spin_lock_irq(&oxfw->lock); 659 + 660 + /* user land lock this */ 661 + if (oxfw->dev_lock_count < 0) { 662 + err = -EBUSY; 663 + goto end; 664 + } 665 + 666 + /* this is the first time */ 667 + if (oxfw->dev_lock_count++ == 0) 668 + snd_oxfw_stream_lock_changed(oxfw); 669 + err = 0; 670 + end: 671 + spin_unlock_irq(&oxfw->lock); 672 + return err; 673 + } 674 + 675 + void snd_oxfw_stream_lock_release(struct snd_oxfw *oxfw) 676 + { 677 + spin_lock_irq(&oxfw->lock); 678 + 679 + if (WARN_ON(oxfw->dev_lock_count <= 0)) 680 + goto end; 681 + if (--oxfw->dev_lock_count == 0) 682 + snd_oxfw_stream_lock_changed(oxfw); 683 + end: 684 + spin_unlock_irq(&oxfw->lock); 685 + }
+5
sound/firewire/oxfw/oxfw.c
··· 139 139 oxfw->unit = unit; 140 140 oxfw->device_info = (const struct device_info *)id->driver_data; 141 141 spin_lock_init(&oxfw->lock); 142 + init_waitqueue_head(&oxfw->hwdep_wait); 142 143 143 144 err = snd_oxfw_stream_discover(oxfw); 144 145 if (err < 0) ··· 162 161 snd_oxfw_proc_init(oxfw); 163 162 164 163 err = snd_oxfw_create_midi(oxfw); 164 + if (err < 0) 165 + goto error; 166 + 167 + err = snd_oxfw_create_hwdep(oxfw); 165 168 if (err < 0) 166 169 goto error; 167 170
+13
sound/firewire/oxfw/oxfw.h
··· 12 12 #include <linux/mod_devicetable.h> 13 13 #include <linux/mutex.h> 14 14 #include <linux/slab.h> 15 + #include <linux/compat.h> 15 16 16 17 #include <sound/control.h> 17 18 #include <sound/core.h> ··· 21 20 #include <sound/pcm_params.h> 22 21 #include <sound/info.h> 23 22 #include <sound/rawmidi.h> 23 + #include <sound/firewire.h> 24 + #include <sound/hwdep.h> 24 25 25 26 #include "../lib.h" 26 27 #include "../fcp.h" ··· 67 64 s16 volume[6]; 68 65 s16 volume_min; 69 66 s16 volume_max; 67 + 68 + int dev_lock_count; 69 + bool dev_lock_changed; 70 + wait_queue_head_t hwdep_wait; 70 71 }; 71 72 72 73 /* ··· 131 124 132 125 int snd_oxfw_stream_discover(struct snd_oxfw *oxfw); 133 126 127 + void snd_oxfw_stream_lock_changed(struct snd_oxfw *oxfw); 128 + int snd_oxfw_stream_lock_try(struct snd_oxfw *oxfw); 129 + void snd_oxfw_stream_lock_release(struct snd_oxfw *oxfw); 130 + 134 131 int snd_oxfw_create_pcm(struct snd_oxfw *oxfw); 135 132 136 133 int snd_oxfw_create_mixer(struct snd_oxfw *oxfw); ··· 142 131 void snd_oxfw_proc_init(struct snd_oxfw *oxfw); 143 132 144 133 int snd_oxfw_create_midi(struct snd_oxfw *oxfw); 134 + 135 + int snd_oxfw_create_hwdep(struct snd_oxfw *oxfw);