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