Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

  • 7e636a7
  • /
  • sound
  • /
  • firewire
  • /
  • tascam
  • /
  • tascam-hwdep.c
Raw File Download

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
  • directory
content badge
swh:1:cnt:74eed9505665c7fb67c9bf6b1f4f3157257ea97a
directory badge
swh:1:dir:4adaeb6495e1a9402e9860b31a7de0ed6a88115a

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
  • directory
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
tascam-hwdep.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * tascam-hwdep.c - a part of driver for TASCAM FireWire series
 *
 * Copyright (c) 2015 Takashi Sakamoto
 */

/*
 * This codes give three functionality.
 *
 * 1.get firewire node information
 * 2.get notification about starting/stopping stream
 * 3.lock/unlock stream
 */

#include "tascam.h"

static long tscm_hwdep_read_locked(struct snd_tscm *tscm, char __user *buf,
				   long count, loff_t *offset)
	__releases(&tscm->lock)
{
	struct snd_firewire_event_lock_status event = {
		.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,
	};

	event.status = (tscm->dev_lock_count > 0);
	tscm->dev_lock_changed = false;
	count = min_t(long, count, sizeof(event));

	spin_unlock_irq(&tscm->lock);

	if (copy_to_user(buf, &event, count))
		return -EFAULT;

	return count;
}

static long tscm_hwdep_read_queue(struct snd_tscm *tscm, char __user *buf,
				  long remained, loff_t *offset)
	__releases(&tscm->lock)
{
	char __user *pos = buf;
	unsigned int type = SNDRV_FIREWIRE_EVENT_TASCAM_CONTROL;
	struct snd_firewire_tascam_change *entries = tscm->queue;
	long count;

	// At least, one control event can be copied.
	if (remained < sizeof(type) + sizeof(*entries)) {
		spin_unlock_irq(&tscm->lock);
		return -EINVAL;
	}

	// Copy the type field later.
	count = sizeof(type);
	remained -= sizeof(type);
	pos += sizeof(type);

	while (true) {
		unsigned int head_pos;
		unsigned int tail_pos;
		unsigned int length;

		if (tscm->pull_pos == tscm->push_pos)
			break;
		else if (tscm->pull_pos < tscm->push_pos)
			tail_pos = tscm->push_pos;
		else
			tail_pos = SND_TSCM_QUEUE_COUNT;
		head_pos = tscm->pull_pos;

		length = (tail_pos - head_pos) * sizeof(*entries);
		if (remained < length)
			length = rounddown(remained, sizeof(*entries));
		if (length == 0)
			break;

		spin_unlock_irq(&tscm->lock);
		if (copy_to_user(pos, &entries[head_pos], length))
			return -EFAULT;

		spin_lock_irq(&tscm->lock);

		tscm->pull_pos = tail_pos % SND_TSCM_QUEUE_COUNT;

		count += length;
		remained -= length;
		pos += length;
	}

	spin_unlock_irq(&tscm->lock);

	if (copy_to_user(buf, &type, sizeof(type)))
		return -EFAULT;

	return count;
}

static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
		       loff_t *offset)
{
	struct snd_tscm *tscm = hwdep->private_data;
	DEFINE_WAIT(wait);

	spin_lock_irq(&tscm->lock);

	while (!tscm->dev_lock_changed && tscm->push_pos == tscm->pull_pos) {
		prepare_to_wait(&tscm->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
		spin_unlock_irq(&tscm->lock);
		schedule();
		finish_wait(&tscm->hwdep_wait, &wait);
		if (signal_pending(current))
			return -ERESTARTSYS;
		spin_lock_irq(&tscm->lock);
	}

	// NOTE: The acquired lock should be released in callee side.
	if (tscm->dev_lock_changed) {
		count = tscm_hwdep_read_locked(tscm, buf, count, offset);
	} else if (tscm->push_pos != tscm->pull_pos) {
		count = tscm_hwdep_read_queue(tscm, buf, count, offset);
	} else {
		spin_unlock_irq(&tscm->lock);
		count = 0;
	}

	return count;
}

static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
			       poll_table *wait)
{
	struct snd_tscm *tscm = hwdep->private_data;
	__poll_t events;

	poll_wait(file, &tscm->hwdep_wait, wait);

	spin_lock_irq(&tscm->lock);
	if (tscm->dev_lock_changed || tscm->push_pos != tscm->pull_pos)
		events = EPOLLIN | EPOLLRDNORM;
	else
		events = 0;
	spin_unlock_irq(&tscm->lock);

	return events;
}

static int hwdep_get_info(struct snd_tscm *tscm, void __user *arg)
{
	struct fw_device *dev = fw_parent_device(tscm->unit);
	struct snd_firewire_get_info info;

	memset(&info, 0, sizeof(info));
	info.type = SNDRV_FIREWIRE_TYPE_TASCAM;
	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]);
	strscpy(info.device_name, dev_name(&dev->device),
		sizeof(info.device_name));

	if (copy_to_user(arg, &info, sizeof(info)))
		return -EFAULT;

	return 0;
}

static int hwdep_lock(struct snd_tscm *tscm)
{
	int err;

	spin_lock_irq(&tscm->lock);

	if (tscm->dev_lock_count == 0) {
		tscm->dev_lock_count = -1;
		err = 0;
	} else {
		err = -EBUSY;
	}

	spin_unlock_irq(&tscm->lock);

	return err;
}

static int hwdep_unlock(struct snd_tscm *tscm)
{
	int err;

	spin_lock_irq(&tscm->lock);

	if (tscm->dev_lock_count == -1) {
		tscm->dev_lock_count = 0;
		err = 0;
	} else {
		err = -EBADFD;
	}

	spin_unlock_irq(&tscm->lock);

	return err;
}

static int tscm_hwdep_state(struct snd_tscm *tscm, void __user *arg)
{
	if (copy_to_user(arg, tscm->state, sizeof(tscm->state)))
		return -EFAULT;

	return 0;
}

static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
{
	struct snd_tscm *tscm = hwdep->private_data;

	spin_lock_irq(&tscm->lock);
	if (tscm->dev_lock_count == -1)
		tscm->dev_lock_count = 0;
	spin_unlock_irq(&tscm->lock);

	return 0;
}

static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
	    unsigned int cmd, unsigned long arg)
{
	struct snd_tscm *tscm = hwdep->private_data;

	switch (cmd) {
	case SNDRV_FIREWIRE_IOCTL_GET_INFO:
		return hwdep_get_info(tscm, (void __user *)arg);
	case SNDRV_FIREWIRE_IOCTL_LOCK:
		return hwdep_lock(tscm);
	case SNDRV_FIREWIRE_IOCTL_UNLOCK:
		return hwdep_unlock(tscm);
	case SNDRV_FIREWIRE_IOCTL_TASCAM_STATE:
		return tscm_hwdep_state(tscm, (void __user *)arg);
	default:
		return -ENOIOCTLCMD;
	}
}

#ifdef CONFIG_COMPAT
static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
			      unsigned int cmd, unsigned long arg)
{
	return hwdep_ioctl(hwdep, file, cmd,
			   (unsigned long)compat_ptr(arg));
}
#else
#define hwdep_compat_ioctl NULL
#endif

int snd_tscm_create_hwdep_device(struct snd_tscm *tscm)
{
	static const struct snd_hwdep_ops ops = {
		.read		= hwdep_read,
		.release	= hwdep_release,
		.poll		= hwdep_poll,
		.ioctl		= hwdep_ioctl,
		.ioctl_compat	= hwdep_compat_ioctl,
	};
	struct snd_hwdep *hwdep;
	int err;

	err = snd_hwdep_new(tscm->card, "Tascam", 0, &hwdep);
	if (err < 0)
		return err;

	strcpy(hwdep->name, "Tascam");
	hwdep->iface = SNDRV_HWDEP_IFACE_FW_TASCAM;
	hwdep->ops = ops;
	hwdep->private_data = tscm;
	hwdep->exclusive = true;

	tscm->hwdep = hwdep;

	return err;
}

back to top

Software Heritage — Copyright (C) 2015–2026, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Content policy— Contact— JavaScript license information— Web API