1da607e19SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
219af57b4STakashi Sakamoto /*
319af57b4STakashi Sakamoto * dice_hwdep.c - a part of driver for DICE based devices
419af57b4STakashi Sakamoto *
519af57b4STakashi Sakamoto * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
619af57b4STakashi Sakamoto * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
719af57b4STakashi Sakamoto */
819af57b4STakashi Sakamoto
919af57b4STakashi Sakamoto #include "dice.h"
1019af57b4STakashi Sakamoto
hwdep_read(struct snd_hwdep * hwdep,char __user * buf,long count,loff_t * offset)1119af57b4STakashi Sakamoto static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf,
1219af57b4STakashi Sakamoto long count, loff_t *offset)
1319af57b4STakashi Sakamoto {
1419af57b4STakashi Sakamoto struct snd_dice *dice = hwdep->private_data;
1519af57b4STakashi Sakamoto DEFINE_WAIT(wait);
1619af57b4STakashi Sakamoto union snd_firewire_event event;
1719af57b4STakashi Sakamoto
1819af57b4STakashi Sakamoto spin_lock_irq(&dice->lock);
1919af57b4STakashi Sakamoto
2019af57b4STakashi Sakamoto while (!dice->dev_lock_changed && dice->notification_bits == 0) {
2119af57b4STakashi Sakamoto prepare_to_wait(&dice->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
2219af57b4STakashi Sakamoto spin_unlock_irq(&dice->lock);
2319af57b4STakashi Sakamoto schedule();
2419af57b4STakashi Sakamoto finish_wait(&dice->hwdep_wait, &wait);
2519af57b4STakashi Sakamoto if (signal_pending(current))
2619af57b4STakashi Sakamoto return -ERESTARTSYS;
2719af57b4STakashi Sakamoto spin_lock_irq(&dice->lock);
2819af57b4STakashi Sakamoto }
2919af57b4STakashi Sakamoto
3019af57b4STakashi Sakamoto memset(&event, 0, sizeof(event));
3119af57b4STakashi Sakamoto if (dice->dev_lock_changed) {
3219af57b4STakashi Sakamoto event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
3319af57b4STakashi Sakamoto event.lock_status.status = dice->dev_lock_count > 0;
3419af57b4STakashi Sakamoto dice->dev_lock_changed = false;
3519af57b4STakashi Sakamoto
3619af57b4STakashi Sakamoto count = min_t(long, count, sizeof(event.lock_status));
3719af57b4STakashi Sakamoto } else {
3819af57b4STakashi Sakamoto event.dice_notification.type =
3919af57b4STakashi Sakamoto SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION;
4019af57b4STakashi Sakamoto event.dice_notification.notification = dice->notification_bits;
4119af57b4STakashi Sakamoto dice->notification_bits = 0;
4219af57b4STakashi Sakamoto
4319af57b4STakashi Sakamoto count = min_t(long, count, sizeof(event.dice_notification));
4419af57b4STakashi Sakamoto }
4519af57b4STakashi Sakamoto
4619af57b4STakashi Sakamoto spin_unlock_irq(&dice->lock);
4719af57b4STakashi Sakamoto
4819af57b4STakashi Sakamoto if (copy_to_user(buf, &event, count))
4919af57b4STakashi Sakamoto return -EFAULT;
5019af57b4STakashi Sakamoto
5119af57b4STakashi Sakamoto return count;
5219af57b4STakashi Sakamoto }
5319af57b4STakashi Sakamoto
hwdep_poll(struct snd_hwdep * hwdep,struct file * file,poll_table * wait)54680ef72aSAl Viro static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
5519af57b4STakashi Sakamoto poll_table *wait)
5619af57b4STakashi Sakamoto {
5719af57b4STakashi Sakamoto struct snd_dice *dice = hwdep->private_data;
58680ef72aSAl Viro __poll_t events;
5919af57b4STakashi Sakamoto
6019af57b4STakashi Sakamoto poll_wait(file, &dice->hwdep_wait, wait);
6119af57b4STakashi Sakamoto
6219af57b4STakashi Sakamoto spin_lock_irq(&dice->lock);
6319af57b4STakashi Sakamoto if (dice->dev_lock_changed || dice->notification_bits != 0)
64a9a08845SLinus Torvalds events = EPOLLIN | EPOLLRDNORM;
6519af57b4STakashi Sakamoto else
6619af57b4STakashi Sakamoto events = 0;
6719af57b4STakashi Sakamoto spin_unlock_irq(&dice->lock);
6819af57b4STakashi Sakamoto
6919af57b4STakashi Sakamoto return events;
7019af57b4STakashi Sakamoto }
7119af57b4STakashi Sakamoto
hwdep_get_info(struct snd_dice * dice,void __user * arg)7219af57b4STakashi Sakamoto static int hwdep_get_info(struct snd_dice *dice, void __user *arg)
7319af57b4STakashi Sakamoto {
7419af57b4STakashi Sakamoto struct fw_device *dev = fw_parent_device(dice->unit);
7519af57b4STakashi Sakamoto struct snd_firewire_get_info info;
7619af57b4STakashi Sakamoto
7719af57b4STakashi Sakamoto memset(&info, 0, sizeof(info));
7819af57b4STakashi Sakamoto info.type = SNDRV_FIREWIRE_TYPE_DICE;
7919af57b4STakashi Sakamoto info.card = dev->card->index;
8019af57b4STakashi Sakamoto *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
8119af57b4STakashi Sakamoto *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
82*75b1a8f9SJoe Perches strscpy(info.device_name, dev_name(&dev->device),
8319af57b4STakashi Sakamoto sizeof(info.device_name));
8419af57b4STakashi Sakamoto
8519af57b4STakashi Sakamoto if (copy_to_user(arg, &info, sizeof(info)))
8619af57b4STakashi Sakamoto return -EFAULT;
8719af57b4STakashi Sakamoto
8819af57b4STakashi Sakamoto return 0;
8919af57b4STakashi Sakamoto }
9019af57b4STakashi Sakamoto
hwdep_lock(struct snd_dice * dice)9119af57b4STakashi Sakamoto static int hwdep_lock(struct snd_dice *dice)
9219af57b4STakashi Sakamoto {
9319af57b4STakashi Sakamoto int err;
9419af57b4STakashi Sakamoto
9519af57b4STakashi Sakamoto spin_lock_irq(&dice->lock);
9619af57b4STakashi Sakamoto
9719af57b4STakashi Sakamoto if (dice->dev_lock_count == 0) {
9819af57b4STakashi Sakamoto dice->dev_lock_count = -1;
9919af57b4STakashi Sakamoto err = 0;
10019af57b4STakashi Sakamoto } else {
10119af57b4STakashi Sakamoto err = -EBUSY;
10219af57b4STakashi Sakamoto }
10319af57b4STakashi Sakamoto
10419af57b4STakashi Sakamoto spin_unlock_irq(&dice->lock);
10519af57b4STakashi Sakamoto
10619af57b4STakashi Sakamoto return err;
10719af57b4STakashi Sakamoto }
10819af57b4STakashi Sakamoto
hwdep_unlock(struct snd_dice * dice)10919af57b4STakashi Sakamoto static int hwdep_unlock(struct snd_dice *dice)
11019af57b4STakashi Sakamoto {
11119af57b4STakashi Sakamoto int err;
11219af57b4STakashi Sakamoto
11319af57b4STakashi Sakamoto spin_lock_irq(&dice->lock);
11419af57b4STakashi Sakamoto
11519af57b4STakashi Sakamoto if (dice->dev_lock_count == -1) {
11619af57b4STakashi Sakamoto dice->dev_lock_count = 0;
11719af57b4STakashi Sakamoto err = 0;
11819af57b4STakashi Sakamoto } else {
11919af57b4STakashi Sakamoto err = -EBADFD;
12019af57b4STakashi Sakamoto }
12119af57b4STakashi Sakamoto
12219af57b4STakashi Sakamoto spin_unlock_irq(&dice->lock);
12319af57b4STakashi Sakamoto
12419af57b4STakashi Sakamoto return err;
12519af57b4STakashi Sakamoto }
12619af57b4STakashi Sakamoto
hwdep_release(struct snd_hwdep * hwdep,struct file * file)12719af57b4STakashi Sakamoto static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
12819af57b4STakashi Sakamoto {
12919af57b4STakashi Sakamoto struct snd_dice *dice = hwdep->private_data;
13019af57b4STakashi Sakamoto
13119af57b4STakashi Sakamoto spin_lock_irq(&dice->lock);
13219af57b4STakashi Sakamoto if (dice->dev_lock_count == -1)
13319af57b4STakashi Sakamoto dice->dev_lock_count = 0;
13419af57b4STakashi Sakamoto spin_unlock_irq(&dice->lock);
13519af57b4STakashi Sakamoto
13619af57b4STakashi Sakamoto return 0;
13719af57b4STakashi Sakamoto }
13819af57b4STakashi Sakamoto
hwdep_ioctl(struct snd_hwdep * hwdep,struct file * file,unsigned int cmd,unsigned long arg)13919af57b4STakashi Sakamoto static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
14019af57b4STakashi Sakamoto unsigned int cmd, unsigned long arg)
14119af57b4STakashi Sakamoto {
14219af57b4STakashi Sakamoto struct snd_dice *dice = hwdep->private_data;
14319af57b4STakashi Sakamoto
14419af57b4STakashi Sakamoto switch (cmd) {
14519af57b4STakashi Sakamoto case SNDRV_FIREWIRE_IOCTL_GET_INFO:
14619af57b4STakashi Sakamoto return hwdep_get_info(dice, (void __user *)arg);
14719af57b4STakashi Sakamoto case SNDRV_FIREWIRE_IOCTL_LOCK:
14819af57b4STakashi Sakamoto return hwdep_lock(dice);
14919af57b4STakashi Sakamoto case SNDRV_FIREWIRE_IOCTL_UNLOCK:
15019af57b4STakashi Sakamoto return hwdep_unlock(dice);
15119af57b4STakashi Sakamoto default:
15219af57b4STakashi Sakamoto return -ENOIOCTLCMD;
15319af57b4STakashi Sakamoto }
15419af57b4STakashi Sakamoto }
15519af57b4STakashi Sakamoto
15619af57b4STakashi Sakamoto #ifdef CONFIG_COMPAT
hwdep_compat_ioctl(struct snd_hwdep * hwdep,struct file * file,unsigned int cmd,unsigned long arg)15719af57b4STakashi Sakamoto static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
15819af57b4STakashi Sakamoto unsigned int cmd, unsigned long arg)
15919af57b4STakashi Sakamoto {
16019af57b4STakashi Sakamoto return hwdep_ioctl(hwdep, file, cmd,
16119af57b4STakashi Sakamoto (unsigned long)compat_ptr(arg));
16219af57b4STakashi Sakamoto }
16319af57b4STakashi Sakamoto #else
16419af57b4STakashi Sakamoto #define hwdep_compat_ioctl NULL
16519af57b4STakashi Sakamoto #endif
16619af57b4STakashi Sakamoto
snd_dice_create_hwdep(struct snd_dice * dice)16719af57b4STakashi Sakamoto int snd_dice_create_hwdep(struct snd_dice *dice)
16819af57b4STakashi Sakamoto {
16919af57b4STakashi Sakamoto static const struct snd_hwdep_ops ops = {
17019af57b4STakashi Sakamoto .read = hwdep_read,
17119af57b4STakashi Sakamoto .release = hwdep_release,
17219af57b4STakashi Sakamoto .poll = hwdep_poll,
17319af57b4STakashi Sakamoto .ioctl = hwdep_ioctl,
17419af57b4STakashi Sakamoto .ioctl_compat = hwdep_compat_ioctl,
17519af57b4STakashi Sakamoto };
17619af57b4STakashi Sakamoto struct snd_hwdep *hwdep;
17719af57b4STakashi Sakamoto int err;
17819af57b4STakashi Sakamoto
17919af57b4STakashi Sakamoto err = snd_hwdep_new(dice->card, "DICE", 0, &hwdep);
18019af57b4STakashi Sakamoto if (err < 0)
18119af57b4STakashi Sakamoto return err;
18219af57b4STakashi Sakamoto strcpy(hwdep->name, "DICE");
18319af57b4STakashi Sakamoto hwdep->iface = SNDRV_HWDEP_IFACE_FW_DICE;
18419af57b4STakashi Sakamoto hwdep->ops = ops;
18519af57b4STakashi Sakamoto hwdep->private_data = dice;
18619af57b4STakashi Sakamoto hwdep->exclusive = true;
18719af57b4STakashi Sakamoto
18819af57b4STakashi Sakamoto return 0;
18919af57b4STakashi Sakamoto }
190