From: Clemens Ladisch <clemens@ladisch.de>
To: Takashi Iwai <tiwai@suse.de>
Cc: alsa-devel@alsa-project.org, linux1394-devel@lists.sourceforge.net
Subject: [PATCH] [07/29] ALSA: dice: implement hwdep device
Date: Mon, 21 Oct 2013 21:24:49 +0200 [thread overview]
Message-ID: <52657F81.3090409@ladisch.de> (raw)
In-Reply-To: <52657E3B.7040205@ladisch.de>
Implement the hwdep locking and notification mechanisms.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
---
sound/firewire/dice.c | 225 ++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 203 insertions(+), 22 deletions(-)
diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index d0575a9..7225878 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -5,6 +5,7 @@
* Licensed under the terms of the GNU General Public License, version 2.
*/
+#include <linux/compat.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/firewire.h>
@@ -13,8 +14,11 @@
#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
#include <sound/control.h>
#include <sound/core.h>
+#include <sound/firewire.h>
#include <sound/hwdep.h>
#include <sound/initval.h>
#include <sound/pcm.h>
@@ -233,13 +237,18 @@
struct dice {
struct snd_card *card;
struct fw_unit *unit;
+ spinlock_t lock;
struct mutex mutex;
unsigned int global_offset;
unsigned int rx_offset;
struct fw_address_handler notification_handler;
int owner_generation;
+ int dev_lock_count; /* > 0 driver, < 0 userspace */
+ bool dev_lock_changed;
bool global_enabled;
bool stream_running;
+ wait_queue_head_t hwdep_wait;
+ u32 notification_bits;
struct snd_pcm_substream *pcm;
struct fw_iso_resources resources;
struct amdtp_out_stream stream;
@@ -259,6 +268,47 @@ static const unsigned int dice_rates[] = {
[6] = 192000,
};
+static void dice_lock_changed(struct dice *dice)
+{
+ dice->dev_lock_changed = true;
+ wake_up(&dice->hwdep_wait);
+}
+
+static int dice_try_lock(struct dice *dice)
+{
+ int err;
+
+ spin_lock_irq(&dice->lock);
+
+ if (dice->dev_lock_count < 0) {
+ err = -EBUSY;
+ goto out;
+ }
+
+ if (dice->dev_lock_count++ == 0)
+ dice_lock_changed(dice);
+ err = 0;
+
+out:
+ spin_unlock_irq(&dice->lock);
+
+ return err;
+}
+
+static void dice_unlock(struct dice *dice)
+{
+ spin_lock_irq(&dice->lock);
+
+ if (WARN_ON(dice->dev_lock_count <= 0))
+ goto out;
+
+ if (--dice->dev_lock_count == 0)
+ dice_lock_changed(dice);
+
+out:
+ spin_unlock_irq(&dice->lock);
+}
+
static inline u64 global_address(struct dice *dice, unsigned int offset)
{
return DICE_PRIVATE_SPACE + dice->global_offset + offset;
@@ -496,6 +546,7 @@ static void dice_notification(struct fw_card *card, struct fw_request *request,
void *data, size_t length, void *callback_data)
{
struct dice *dice = callback_data;
+ unsigned long flags;
if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
fw_send_response(card, request, RCODE_TYPE_ERROR);
@@ -505,9 +556,11 @@ static void dice_notification(struct fw_card *card, struct fw_request *request,
fw_send_response(card, request, RCODE_ADDRESS_ERROR);
return;
}
- dev_dbg(&dice->unit->device,
- "notification: %08x\n", be32_to_cpup(data));
+ spin_lock_irqsave(&dice->lock, flags);
+ dice->notification_bits |= be32_to_cpup(data);
+ spin_unlock_irqrestore(&dice->lock, flags);
fw_send_response(card, request, RCODE_COMPLETE);
+ wake_up(&dice->hwdep_wait);
}
static int dice_open(struct snd_pcm_substream *substream)
@@ -531,26 +584,32 @@ static int dice_open(struct snd_pcm_substream *substream)
unsigned int rate;
int err;
+ err = dice_try_lock(dice);
+ if (err < 0)
+ goto error;
+
err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
global_address(dice, GLOBAL_CLOCK_SELECT),
&clock_sel, 4);
if (err < 0)
- return err;
+ goto err_lock;
rate = (be32_to_cpu(clock_sel) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT;
- if (rate >= ARRAY_SIZE(dice_rates))
- return -ENXIO;
+ if (rate >= ARRAY_SIZE(dice_rates)) {
+ err = -ENXIO;
+ goto err_lock;
+ }
rate = dice_rates[rate];
err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
rx_address(dice, RX_NUMBER_AUDIO),
&number_audio, 4);
if (err < 0)
- return err;
+ goto err_lock;
err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
rx_address(dice, RX_NUMBER_MIDI),
&number_midi, 4);
if (err < 0)
- return err;
+ goto err_lock;
runtime->hw = hardware;
@@ -568,17 +627,26 @@ static int dice_open(struct snd_pcm_substream *substream)
SNDRV_PCM_HW_PARAM_PERIOD_TIME,
5000, 8192000);
if (err < 0)
- return err;
+ goto err_lock;
err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
if (err < 0)
- return err;
+ goto err_lock;
return 0;
+
+err_lock:
+ dice_unlock(dice);
+error:
+ return err;
}
static int dice_close(struct snd_pcm_substream *substream)
{
+ struct dice *dice = substream->private_data;
+
+ dice_unlock(dice);
+
return 0;
}
@@ -783,45 +851,156 @@ static int dice_create_pcm(struct dice *dice)
return 0;
}
-// TODO: implement these
-
static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf,
long count, loff_t *offset)
{
- return -EIO;
+ struct dice *dice = hwdep->private_data;
+ DEFINE_WAIT(wait);
+ union snd_firewire_event event;
+
+ spin_lock_irq(&dice->lock);
+
+ while (!dice->dev_lock_changed && dice->notification_bits == 0) {
+ prepare_to_wait(&dice->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&dice->lock);
+ schedule();
+ finish_wait(&dice->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&dice->lock);
+ }
+
+ memset(&event, 0, sizeof(event));
+ if (dice->dev_lock_changed) {
+ event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+ event.lock_status.status = dice->dev_lock_count > 0;
+ dice->dev_lock_changed = false;
+
+ count = min(count, (long)sizeof(event.lock_status));
+ } else {
+ event.dice_notification.type = SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION;
+ event.dice_notification.notification = dice->notification_bits;
+ dice->notification_bits = 0;
+
+ count = min(count, (long)sizeof(event.dice_notification));
+ }
+
+ spin_unlock_irq(&dice->lock);
+
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+
+ return count;
}
-static int dice_hwdep_open(struct snd_hwdep *hwdep, struct file *file)
+static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+ poll_table *wait)
{
- return -EIO;
+ struct dice *dice = hwdep->private_data;
+ unsigned int events;
+
+ poll_wait(file, &dice->hwdep_wait, wait);
+
+ spin_lock_irq(&dice->lock);
+ if (dice->dev_lock_changed || dice->notification_bits != 0)
+ events = POLLIN | POLLRDNORM;
+ else
+ events = 0;
+ spin_unlock_irq(&dice->lock);
+
+ return events;
}
-static int dice_hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+static int dice_hwdep_get_info(struct dice *dice, void __user *arg)
{
+ struct fw_device *dev = fw_parent_device(dice->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_DICE;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strlcpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
return 0;
}
-static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
- poll_table *wait)
+static int dice_hwdep_lock(struct dice *dice)
+{
+ int err;
+
+ spin_lock_irq(&dice->lock);
+
+ if (dice->dev_lock_count == 0) {
+ dice->dev_lock_count = -1;
+ err = 0;
+ } else {
+ err = -EBUSY;
+ }
+
+ spin_unlock_irq(&dice->lock);
+
+ return err;
+}
+
+static int dice_hwdep_unlock(struct dice *dice)
{
- return POLLERR | POLLHUP;
+ int err;
+
+ spin_lock_irq(&dice->lock);
+
+ if (dice->dev_lock_count == -1) {
+ dice->dev_lock_count = 0;
+ err = 0;
+ } else {
+ err = -EBADFD;
+ }
+
+ spin_unlock_irq(&dice->lock);
+
+ return err;
}
static int dice_hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
unsigned int cmd, unsigned long arg)
{
- return -EIO;
+ struct dice *dice = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return dice_hwdep_get_info(dice, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return dice_hwdep_lock(dice);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return dice_hwdep_unlock(dice);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int dice_hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return dice_hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
}
+#else
+#define dice_hwdep_compat_ioctl NULL
+#endif
static int dice_create_hwdep(struct dice *dice)
{
static const struct snd_hwdep_ops ops = {
.read = dice_hwdep_read,
- .open = dice_hwdep_open,
- .release = dice_hwdep_release,
.poll = dice_hwdep_poll,
.ioctl = dice_hwdep_ioctl,
- .ioctl_compat = dice_hwdep_ioctl,
+ .ioctl_compat = dice_hwdep_compat_ioctl,
};
struct snd_hwdep *hwdep;
int err;
@@ -922,8 +1101,10 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
dice = card->private_data;
dice->card = card;
+ spin_lock_init(&dice->lock);
mutex_init(&dice->mutex);
dice->unit = unit;
+ init_waitqueue_head(&dice->hwdep_wait);
err = dice_init_offsets(dice);
if (err < 0)
------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk
next prev parent reply other threads:[~2013-10-21 19:24 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-10-21 19:19 [GIT PULL] [00/29] playback-only DICE driver Clemens Ladisch
2013-10-21 19:21 ` [PATCH] [01/29] ALSA: add " Clemens Ladisch
2013-10-21 19:21 ` [PATCH] [02/29] ALSA: dice: optimize bus reset handling Clemens Ladisch
2013-10-21 19:22 ` [PATCH] [03/29] ALSA: dice: allow all sample rates Clemens Ladisch
2013-10-21 19:23 ` [PATCH] [04/29] ALSA: dice: reduce noisy logging Clemens Ladisch
2013-10-21 19:23 ` [PATCH] [05/29] ALSA: dice, firewire-lib: add blocking mode Clemens Ladisch
2013-10-21 19:24 ` [PATCH] [06/29] ALSA: dice: fix hang when unplugging a running device Clemens Ladisch
2013-10-21 19:24 ` Clemens Ladisch [this message]
2013-10-21 19:25 ` [PATCH] [08/29] ALSA: dice: clear device lock when closing hwdep device Clemens Ladisch
2013-10-21 19:25 ` [PATCH] [09/29] ALSA: firewire: introduce amdtp_out_stream_running() Clemens Ladisch
2013-10-21 19:26 ` [PATCH] [10/29] ALSA: dice: reorganize interface definitions Clemens Ladisch
2013-10-21 19:26 ` [PATCH] [11/29] ALSA: dice: fix device detection for other vendors Clemens Ladisch
2013-10-21 19:27 ` [PATCH] [12/29] ALSA: dice: support dual-wire stream format at 192 kHz Clemens Ladisch
2013-10-21 19:27 ` [PATCH] [13/29] ALSA: dice: optimize reading of consecutive registers Clemens Ladisch
2013-10-21 19:28 ` [PATCH] [14/29] ALSA: firewire: extend snd_fw_transaction() Clemens Ladisch
2013-10-21 19:29 ` [PATCH] [15/29] ALSA: dice: avoid superflous write at bus reset Clemens Ladisch
2013-10-21 19:29 ` [PATCH] [16/29] ALSA: dice: remove 10s period length limit Clemens Ladisch
2013-10-21 19:30 ` [PATCH] [17/29] ALSA: dice: remove superfluous field Clemens Ladisch
2013-10-21 19:31 ` [PATCH] [18/29] ALSA: dice: fix locking Clemens Ladisch
2013-10-21 19:31 ` [PATCH] [19/29] ALSA: dice: make amdtp_rates[] const Clemens Ladisch
2013-10-21 19:32 ` [PATCH] [20/29] ALSA: dice: get clock capabilities Clemens Ladisch
2013-10-21 19:32 ` [PATCH] [21/29] ALSA: dice: allow notifications during initialization Clemens Ladisch
2013-10-21 19:33 ` [PATCH] [22/29] ALSA: dice: get rate-dependent parameters Clemens Ladisch
2013-10-21 19:34 ` [PATCH] [23/29] ALSA: dice: dynamic sample rate selection Clemens Ladisch
2013-10-21 19:34 ` [PATCH] [24/29] ALSA: dice: check clock change timeout Clemens Ladisch
2013-10-21 19:34 ` [PATCH] [25/29] ALSA: dice: add a proc file to show device information Clemens Ladisch
2013-10-21 19:35 ` [PATCH] [26/29] ALSA: dice: document quadlet alignment Clemens Ladisch
2013-10-21 19:40 ` [PATCH] [27/29] ALSA: dice: dice_proc_read: remove wrong typecast Clemens Ladisch
2013-10-21 19:41 ` [PATCH] [28/29] ALSA: dice: fix detection of Weiss devices Clemens Ladisch
2013-10-21 19:42 ` [PATCH] [29/29] ALSA: dice: restrict the driver to playback-only devices Clemens Ladisch
2013-10-24 9:39 ` [GIT PULL] [00/29] playback-only DICE driver Takashi Iwai
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=52657F81.3090409@ladisch.de \
--to=clemens@ladisch.de \
--cc=alsa-devel@alsa-project.org \
--cc=linux1394-devel@lists.sourceforge.net \
--cc=tiwai@suse.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.