cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ff-hwdep.c (4014B)


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