Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * digi00x-hwdep.c - a part of driver for Digidesign Digi 002/003 family
0004  *
0005  * Copyright (c) 2014-2015 Takashi Sakamoto
0006  */
0007 
0008 /*
0009  * This codes give three functionality.
0010  *
0011  * 1.get firewire node information
0012  * 2.get notification about starting/stopping stream
0013  * 3.lock/unlock stream
0014  * 4.get asynchronous messaging
0015  */
0016 
0017 #include "digi00x.h"
0018 
0019 static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf,  long count,
0020                loff_t *offset)
0021 {
0022     struct snd_dg00x *dg00x = hwdep->private_data;
0023     DEFINE_WAIT(wait);
0024     union snd_firewire_event event;
0025 
0026     spin_lock_irq(&dg00x->lock);
0027 
0028     while (!dg00x->dev_lock_changed && dg00x->msg == 0) {
0029         prepare_to_wait(&dg00x->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
0030         spin_unlock_irq(&dg00x->lock);
0031         schedule();
0032         finish_wait(&dg00x->hwdep_wait, &wait);
0033         if (signal_pending(current))
0034             return -ERESTARTSYS;
0035         spin_lock_irq(&dg00x->lock);
0036     }
0037 
0038     memset(&event, 0, sizeof(event));
0039     if (dg00x->dev_lock_changed) {
0040         event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
0041         event.lock_status.status = (dg00x->dev_lock_count > 0);
0042         dg00x->dev_lock_changed = false;
0043 
0044         count = min_t(long, count, sizeof(event.lock_status));
0045     } else {
0046         event.digi00x_message.type =
0047                     SNDRV_FIREWIRE_EVENT_DIGI00X_MESSAGE;
0048         event.digi00x_message.message = dg00x->msg;
0049         dg00x->msg = 0;
0050 
0051         count = min_t(long, count, sizeof(event.digi00x_message));
0052     }
0053 
0054     spin_unlock_irq(&dg00x->lock);
0055 
0056     if (copy_to_user(buf, &event, count))
0057         return -EFAULT;
0058 
0059     return count;
0060 }
0061 
0062 static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
0063                    poll_table *wait)
0064 {
0065     struct snd_dg00x *dg00x = hwdep->private_data;
0066     __poll_t events;
0067 
0068     poll_wait(file, &dg00x->hwdep_wait, wait);
0069 
0070     spin_lock_irq(&dg00x->lock);
0071     if (dg00x->dev_lock_changed || dg00x->msg)
0072         events = EPOLLIN | EPOLLRDNORM;
0073     else
0074         events = 0;
0075     spin_unlock_irq(&dg00x->lock);
0076 
0077     return events;
0078 }
0079 
0080 static int hwdep_get_info(struct snd_dg00x *dg00x, void __user *arg)
0081 {
0082     struct fw_device *dev = fw_parent_device(dg00x->unit);
0083     struct snd_firewire_get_info info;
0084 
0085     memset(&info, 0, sizeof(info));
0086     info.type = SNDRV_FIREWIRE_TYPE_DIGI00X;
0087     info.card = dev->card->index;
0088     *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
0089     *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
0090     strscpy(info.device_name, dev_name(&dev->device),
0091         sizeof(info.device_name));
0092 
0093     if (copy_to_user(arg, &info, sizeof(info)))
0094         return -EFAULT;
0095 
0096     return 0;
0097 }
0098 
0099 static int hwdep_lock(struct snd_dg00x *dg00x)
0100 {
0101     int err;
0102 
0103     spin_lock_irq(&dg00x->lock);
0104 
0105     if (dg00x->dev_lock_count == 0) {
0106         dg00x->dev_lock_count = -1;
0107         err = 0;
0108     } else {
0109         err = -EBUSY;
0110     }
0111 
0112     spin_unlock_irq(&dg00x->lock);
0113 
0114     return err;
0115 }
0116 
0117 static int hwdep_unlock(struct snd_dg00x *dg00x)
0118 {
0119     int err;
0120 
0121     spin_lock_irq(&dg00x->lock);
0122 
0123     if (dg00x->dev_lock_count == -1) {
0124         dg00x->dev_lock_count = 0;
0125         err = 0;
0126     } else {
0127         err = -EBADFD;
0128     }
0129 
0130     spin_unlock_irq(&dg00x->lock);
0131 
0132     return err;
0133 }
0134 
0135 static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
0136 {
0137     struct snd_dg00x *dg00x = hwdep->private_data;
0138 
0139     spin_lock_irq(&dg00x->lock);
0140     if (dg00x->dev_lock_count == -1)
0141         dg00x->dev_lock_count = 0;
0142     spin_unlock_irq(&dg00x->lock);
0143 
0144     return 0;
0145 }
0146 
0147 static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
0148         unsigned int cmd, unsigned long arg)
0149 {
0150     struct snd_dg00x *dg00x = hwdep->private_data;
0151 
0152     switch (cmd) {
0153     case SNDRV_FIREWIRE_IOCTL_GET_INFO:
0154         return hwdep_get_info(dg00x, (void __user *)arg);
0155     case SNDRV_FIREWIRE_IOCTL_LOCK:
0156         return hwdep_lock(dg00x);
0157     case SNDRV_FIREWIRE_IOCTL_UNLOCK:
0158         return hwdep_unlock(dg00x);
0159     default:
0160         return -ENOIOCTLCMD;
0161     }
0162 }
0163 
0164 #ifdef CONFIG_COMPAT
0165 static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
0166                   unsigned int cmd, unsigned long arg)
0167 {
0168     return hwdep_ioctl(hwdep, file, cmd,
0169                (unsigned long)compat_ptr(arg));
0170 }
0171 #else
0172 #define hwdep_compat_ioctl NULL
0173 #endif
0174 
0175 int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x)
0176 {
0177     static const struct snd_hwdep_ops ops = {
0178         .read       = hwdep_read,
0179         .release    = hwdep_release,
0180         .poll       = hwdep_poll,
0181         .ioctl      = hwdep_ioctl,
0182         .ioctl_compat   = hwdep_compat_ioctl,
0183     };
0184     struct snd_hwdep *hwdep;
0185     int err;
0186 
0187     err = snd_hwdep_new(dg00x->card, "Digi00x", 0, &hwdep);
0188     if (err < 0)
0189         return err;
0190 
0191     strcpy(hwdep->name, "Digi00x");
0192     hwdep->iface = SNDRV_HWDEP_IFACE_FW_DIGI00X;
0193     hwdep->ops = ops;
0194     hwdep->private_data = dg00x;
0195     hwdep->exclusive = true;
0196 
0197     return err;
0198 }