2 * fireworks_hwdep.c - a part of driver for Fireworks based devices
4 * Copyright (c) 2013-2014 Takashi Sakamoto
6 * Licensed under the terms of the GNU General Public License, version 2.
10 * This codes have five functionalities.
12 * 1.get information about firewire node
13 * 2.get notification about starting/stopping stream
14 * 3.lock/unlock streaming
15 * 4.transmit command of EFW transaction
16 * 5.receive response of EFW transaction
20 #include "fireworks.h"
23 hwdep_read_resp_buf(struct snd_efw
*efw
, char __user
*buf
, long remained
,
26 unsigned int length
, till_end
, type
;
27 struct snd_efw_transaction
*t
;
31 if (remained
< sizeof(type
) + sizeof(struct snd_efw_transaction
))
34 /* data type is SNDRV_FIREWIRE_EVENT_EFW_RESPONSE */
35 type
= SNDRV_FIREWIRE_EVENT_EFW_RESPONSE
;
36 if (copy_to_user(buf
, &type
, sizeof(type
)))
38 remained
-= sizeof(type
);
41 /* write into buffer as many responses as possible */
42 spin_lock_irq(&efw
->lock
);
45 * When another task reaches here during this task's access to user
46 * space, it picks up current position in buffer and can read the same
47 * series of responses.
49 pull_ptr
= efw
->pull_ptr
;
51 while (efw
->push_ptr
!= pull_ptr
) {
52 t
= (struct snd_efw_transaction
*)(pull_ptr
);
53 length
= be32_to_cpu(t
->length
) * sizeof(__be32
);
55 /* confirm enough space for this response */
56 if (remained
< length
)
59 /* copy from ring buffer to user buffer */
61 till_end
= snd_efw_resp_buf_size
-
62 (unsigned int)(pull_ptr
- efw
->resp_buf
);
63 till_end
= min_t(unsigned int, length
, till_end
);
65 spin_unlock_irq(&efw
->lock
);
67 if (copy_to_user(buf
, pull_ptr
, till_end
))
70 spin_lock_irq(&efw
->lock
);
73 if (pull_ptr
>= efw
->resp_buf
+ snd_efw_resp_buf_size
)
74 pull_ptr
-= snd_efw_resp_buf_size
;
84 * All of tasks can read from the buffer nearly simultaneously, but the
85 * last position for each task is different depending on the length of
86 * given buffer. Here, for simplicity, a position of buffer is set by
87 * the latest task. It's better for a listening application to allow one
88 * thread to read from the buffer. Unless, each task can read different
89 * sequence of responses depending on variation of buffer length.
91 efw
->pull_ptr
= pull_ptr
;
93 spin_unlock_irq(&efw
->lock
);
99 hwdep_read_locked(struct snd_efw
*efw
, char __user
*buf
, long count
,
102 union snd_firewire_event event
= {
103 .lock_status
.type
= SNDRV_FIREWIRE_EVENT_LOCK_STATUS
,
106 spin_lock_irq(&efw
->lock
);
108 event
.lock_status
.status
= (efw
->dev_lock_count
> 0);
109 efw
->dev_lock_changed
= false;
111 spin_unlock_irq(&efw
->lock
);
113 count
= min_t(long, count
, sizeof(event
.lock_status
));
115 if (copy_to_user(buf
, &event
, count
))
122 hwdep_read(struct snd_hwdep
*hwdep
, char __user
*buf
, long count
,
125 struct snd_efw
*efw
= hwdep
->private_data
;
127 bool dev_lock_changed
;
130 spin_lock_irq(&efw
->lock
);
132 dev_lock_changed
= efw
->dev_lock_changed
;
133 queued
= efw
->push_ptr
!= efw
->pull_ptr
;
135 while (!dev_lock_changed
&& !queued
) {
136 prepare_to_wait(&efw
->hwdep_wait
, &wait
, TASK_INTERRUPTIBLE
);
137 spin_unlock_irq(&efw
->lock
);
139 finish_wait(&efw
->hwdep_wait
, &wait
);
140 if (signal_pending(current
))
142 spin_lock_irq(&efw
->lock
);
143 dev_lock_changed
= efw
->dev_lock_changed
;
144 queued
= efw
->push_ptr
!= efw
->pull_ptr
;
147 spin_unlock_irq(&efw
->lock
);
149 if (dev_lock_changed
)
150 count
= hwdep_read_locked(efw
, buf
, count
, offset
);
152 count
= hwdep_read_resp_buf(efw
, buf
, count
, offset
);
158 hwdep_write(struct snd_hwdep
*hwdep
, const char __user
*data
, long count
,
161 struct snd_efw
*efw
= hwdep
->private_data
;
165 if (count
< sizeof(struct snd_efw_transaction
) ||
166 SND_EFW_RESPONSE_MAXIMUM_BYTES
< count
)
169 buf
= memdup_user(data
, count
);
173 /* check seqnum is not for kernel-land */
174 seqnum
= be32_to_cpu(((struct snd_efw_transaction
*)buf
)->seqnum
);
175 if (seqnum
> SND_EFW_TRANSACTION_USER_SEQNUM_MAX
) {
180 if (snd_efw_transaction_cmd(efw
->unit
, buf
, count
) < 0)
188 hwdep_poll(struct snd_hwdep
*hwdep
, struct file
*file
, poll_table
*wait
)
190 struct snd_efw
*efw
= hwdep
->private_data
;
193 poll_wait(file
, &efw
->hwdep_wait
, wait
);
195 spin_lock_irq(&efw
->lock
);
196 if (efw
->dev_lock_changed
|| efw
->pull_ptr
!= efw
->push_ptr
)
197 events
= EPOLLIN
| EPOLLRDNORM
;
200 spin_unlock_irq(&efw
->lock
);
202 return events
| EPOLLOUT
;
206 hwdep_get_info(struct snd_efw
*efw
, void __user
*arg
)
208 struct fw_device
*dev
= fw_parent_device(efw
->unit
);
209 struct snd_firewire_get_info info
;
211 memset(&info
, 0, sizeof(info
));
212 info
.type
= SNDRV_FIREWIRE_TYPE_FIREWORKS
;
213 info
.card
= dev
->card
->index
;
214 *(__be32
*)&info
.guid
[0] = cpu_to_be32(dev
->config_rom
[3]);
215 *(__be32
*)&info
.guid
[4] = cpu_to_be32(dev
->config_rom
[4]);
216 strlcpy(info
.device_name
, dev_name(&dev
->device
),
217 sizeof(info
.device_name
));
219 if (copy_to_user(arg
, &info
, sizeof(info
)))
226 hwdep_lock(struct snd_efw
*efw
)
230 spin_lock_irq(&efw
->lock
);
232 if (efw
->dev_lock_count
== 0) {
233 efw
->dev_lock_count
= -1;
239 spin_unlock_irq(&efw
->lock
);
245 hwdep_unlock(struct snd_efw
*efw
)
249 spin_lock_irq(&efw
->lock
);
251 if (efw
->dev_lock_count
== -1) {
252 efw
->dev_lock_count
= 0;
258 spin_unlock_irq(&efw
->lock
);
264 hwdep_release(struct snd_hwdep
*hwdep
, struct file
*file
)
266 struct snd_efw
*efw
= hwdep
->private_data
;
268 spin_lock_irq(&efw
->lock
);
269 if (efw
->dev_lock_count
== -1)
270 efw
->dev_lock_count
= 0;
271 spin_unlock_irq(&efw
->lock
);
277 hwdep_ioctl(struct snd_hwdep
*hwdep
, struct file
*file
,
278 unsigned int cmd
, unsigned long arg
)
280 struct snd_efw
*efw
= hwdep
->private_data
;
283 case SNDRV_FIREWIRE_IOCTL_GET_INFO
:
284 return hwdep_get_info(efw
, (void __user
*)arg
);
285 case SNDRV_FIREWIRE_IOCTL_LOCK
:
286 return hwdep_lock(efw
);
287 case SNDRV_FIREWIRE_IOCTL_UNLOCK
:
288 return hwdep_unlock(efw
);
296 hwdep_compat_ioctl(struct snd_hwdep
*hwdep
, struct file
*file
,
297 unsigned int cmd
, unsigned long arg
)
299 return hwdep_ioctl(hwdep
, file
, cmd
,
300 (unsigned long)compat_ptr(arg
));
303 #define hwdep_compat_ioctl NULL
306 int snd_efw_create_hwdep_device(struct snd_efw
*efw
)
308 static const struct snd_hwdep_ops ops
= {
310 .write
= hwdep_write
,
311 .release
= hwdep_release
,
313 .ioctl
= hwdep_ioctl
,
314 .ioctl_compat
= hwdep_compat_ioctl
,
316 struct snd_hwdep
*hwdep
;
319 err
= snd_hwdep_new(efw
->card
, "Fireworks", 0, &hwdep
);
322 strcpy(hwdep
->name
, "Fireworks");
323 hwdep
->iface
= SNDRV_HWDEP_IFACE_FW_FIREWORKS
;
325 hwdep
->private_data
= efw
;
326 hwdep
->exclusive
= true;