2 * tascam-hwdep.c - a part of driver for TASCAM FireWire series
4 * Copyright (c) 2015 Takashi Sakamoto
6 * Licensed under the terms of the GNU General Public License, version 2.
10 * This codes give three functionality.
12 * 1.get firewire node information
13 * 2.get notification about starting/stopping stream
14 * 3.lock/unlock stream
19 static long tscm_hwdep_read_locked(struct snd_tscm *tscm, char __user *buf,
20 long count, loff_t *offset)
22 struct snd_firewire_event_lock_status event = {
23 .type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,
26 event.status = (tscm->dev_lock_count > 0);
27 tscm->dev_lock_changed = false;
28 count = min_t(long, count, sizeof(event));
30 spin_unlock_irq(&tscm->lock);
32 if (copy_to_user(buf, &event, count))
38 static long tscm_hwdep_read_queue(struct snd_tscm *tscm, char __user *buf,
39 long remained, loff_t *offset)
41 char __user *pos = buf;
42 unsigned int type = SNDRV_FIREWIRE_EVENT_TASCAM_CONTROL;
43 struct snd_firewire_tascam_change *entries = tscm->queue;
46 // At least, one control event can be copied.
47 if (remained < sizeof(type) + sizeof(*entries)) {
48 spin_unlock_irq(&tscm->lock);
52 // Copy the type field later.
54 remained -= sizeof(type);
58 unsigned int head_pos;
59 unsigned int tail_pos;
62 if (tscm->pull_pos == tscm->push_pos)
64 else if (tscm->pull_pos < tscm->push_pos)
65 tail_pos = tscm->push_pos;
67 tail_pos = SND_TSCM_QUEUE_COUNT;
68 head_pos = tscm->pull_pos;
70 length = (tail_pos - head_pos) * sizeof(*entries);
71 if (remained < length)
72 length = rounddown(remained, sizeof(*entries));
76 spin_unlock_irq(&tscm->lock);
77 if (copy_to_user(pos, &entries[head_pos], length))
80 spin_lock_irq(&tscm->lock);
82 tscm->pull_pos = tail_pos % SND_TSCM_QUEUE_COUNT;
89 spin_unlock_irq(&tscm->lock);
91 if (copy_to_user(buf, &type, sizeof(type)))
97 static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
100 struct snd_tscm *tscm = hwdep->private_data;
103 spin_lock_irq(&tscm->lock);
105 while (!tscm->dev_lock_changed && tscm->push_pos == tscm->pull_pos) {
106 prepare_to_wait(&tscm->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
107 spin_unlock_irq(&tscm->lock);
109 finish_wait(&tscm->hwdep_wait, &wait);
110 if (signal_pending(current))
112 spin_lock_irq(&tscm->lock);
115 // NOTE: The acquired lock should be released in callee side.
116 if (tscm->dev_lock_changed) {
117 count = tscm_hwdep_read_locked(tscm, buf, count, offset);
118 } else if (tscm->push_pos != tscm->pull_pos) {
119 count = tscm_hwdep_read_queue(tscm, buf, count, offset);
121 spin_unlock_irq(&tscm->lock);
128 static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
131 struct snd_tscm *tscm = hwdep->private_data;
134 poll_wait(file, &tscm->hwdep_wait, wait);
136 spin_lock_irq(&tscm->lock);
137 if (tscm->dev_lock_changed || tscm->push_pos != tscm->pull_pos)
138 events = EPOLLIN | EPOLLRDNORM;
141 spin_unlock_irq(&tscm->lock);
146 static int hwdep_get_info(struct snd_tscm *tscm, void __user *arg)
148 struct fw_device *dev = fw_parent_device(tscm->unit);
149 struct snd_firewire_get_info info;
151 memset(&info, 0, sizeof(info));
152 info.type = SNDRV_FIREWIRE_TYPE_TASCAM;
153 info.card = dev->card->index;
154 *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
155 *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
156 strlcpy(info.device_name, dev_name(&dev->device),
157 sizeof(info.device_name));
159 if (copy_to_user(arg, &info, sizeof(info)))
165 static int hwdep_lock(struct snd_tscm *tscm)
169 spin_lock_irq(&tscm->lock);
171 if (tscm->dev_lock_count == 0) {
172 tscm->dev_lock_count = -1;
178 spin_unlock_irq(&tscm->lock);
183 static int hwdep_unlock(struct snd_tscm *tscm)
187 spin_lock_irq(&tscm->lock);
189 if (tscm->dev_lock_count == -1) {
190 tscm->dev_lock_count = 0;
196 spin_unlock_irq(&tscm->lock);
201 static int tscm_hwdep_state(struct snd_tscm *tscm, void __user *arg)
203 if (copy_to_user(arg, tscm->state, sizeof(tscm->state)))
209 static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
211 struct snd_tscm *tscm = hwdep->private_data;
213 spin_lock_irq(&tscm->lock);
214 if (tscm->dev_lock_count == -1)
215 tscm->dev_lock_count = 0;
216 spin_unlock_irq(&tscm->lock);
221 static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
222 unsigned int cmd, unsigned long arg)
224 struct snd_tscm *tscm = hwdep->private_data;
227 case SNDRV_FIREWIRE_IOCTL_GET_INFO:
228 return hwdep_get_info(tscm, (void __user *)arg);
229 case SNDRV_FIREWIRE_IOCTL_LOCK:
230 return hwdep_lock(tscm);
231 case SNDRV_FIREWIRE_IOCTL_UNLOCK:
232 return hwdep_unlock(tscm);
233 case SNDRV_FIREWIRE_IOCTL_TASCAM_STATE:
234 return tscm_hwdep_state(tscm, (void __user *)arg);
241 static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
242 unsigned int cmd, unsigned long arg)
244 return hwdep_ioctl(hwdep, file, cmd,
245 (unsigned long)compat_ptr(arg));
248 #define hwdep_compat_ioctl NULL
251 int snd_tscm_create_hwdep_device(struct snd_tscm *tscm)
253 static const struct snd_hwdep_ops ops = {
255 .release = hwdep_release,
257 .ioctl = hwdep_ioctl,
258 .ioctl_compat = hwdep_compat_ioctl,
260 struct snd_hwdep *hwdep;
263 err = snd_hwdep_new(tscm->card, "Tascam", 0, &hwdep);
267 strcpy(hwdep->name, "Tascam");
268 hwdep->iface = SNDRV_HWDEP_IFACE_FW_TASCAM;
270 hwdep->private_data = tscm;
271 hwdep->exclusive = true;