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

digi00x-hwdep.c (4462B)


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