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