199 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * digi00x-hwdep.c - a part of driver for Digidesign Digi 002/003 family4 *5 * Copyright (c) 2014-2015 Takashi Sakamoto6 */7 8/*9 * This codes give three functionality.10 *11 * 1.get firewire node information12 * 2.get notification about starting/stopping stream13 * 3.lock/unlock stream14 * 4.get asynchronous messaging15 */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 else74 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_COMPAT165static 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#else172#define hwdep_compat_ioctl NULL173#endif174 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}199