1 // SPDX-License-Identifier: GPL-2.0-only
3 * fireworks_hwdep.c - a part of driver for Fireworks based devices
5 * Copyright (c) 2013-2014 Takashi Sakamoto
9 * This codes have five functionalities.
11 * 1.get information about firewire node
12 * 2.get notification about starting/stopping stream
13 * 3.lock/unlock streaming
14 * 4.transmit command of EFW transaction
15 * 5.receive response of EFW transaction
19 #include "fireworks.h"
22 hwdep_read_resp_buf(struct snd_efw
*efw
, char __user
*buf
, long remained
,
25 unsigned int length
, till_end
, type
;
26 struct snd_efw_transaction
*t
;
30 if (remained
< sizeof(type
) + sizeof(struct snd_efw_transaction
))
33 /* data type is SNDRV_FIREWIRE_EVENT_EFW_RESPONSE */
34 type
= SNDRV_FIREWIRE_EVENT_EFW_RESPONSE
;
35 if (copy_to_user(buf
, &type
, sizeof(type
)))
37 remained
-= sizeof(type
);
40 /* write into buffer as many responses as possible */
41 spin_lock_irq(&efw
->lock
);
44 * When another task reaches here during this task's access to user
45 * space, it picks up current position in buffer and can read the same
46 * series of responses.
48 pull_ptr
= efw
->pull_ptr
;
50 while (efw
->push_ptr
!= pull_ptr
) {
51 t
= (struct snd_efw_transaction
*)(pull_ptr
);
52 length
= be32_to_cpu(t
->length
) * sizeof(__be32
);
54 /* confirm enough space for this response */
55 if (remained
< length
)
58 /* copy from ring buffer to user buffer */
60 till_end
= snd_efw_resp_buf_size
-
61 (unsigned int)(pull_ptr
- efw
->resp_buf
);
62 till_end
= min_t(unsigned int, length
, till_end
);
64 spin_unlock_irq(&efw
->lock
);
66 if (copy_to_user(buf
, pull_ptr
, till_end
))
69 spin_lock_irq(&efw
->lock
);
72 if (pull_ptr
>= efw
->resp_buf
+ snd_efw_resp_buf_size
)
73 pull_ptr
-= snd_efw_resp_buf_size
;
83 * All of tasks can read from the buffer nearly simultaneously, but the
84 * last position for each task is different depending on the length of
85 * given buffer. Here, for simplicity, a position of buffer is set by
86 * the latest task. It's better for a listening application to allow one
87 * thread to read from the buffer. Unless, each task can read different
88 * sequence of responses depending on variation of buffer length.
90 efw
->pull_ptr
= pull_ptr
;
92 spin_unlock_irq(&efw
->lock
);
98 hwdep_read_locked(struct snd_efw
*efw
, char __user
*buf
, long count
,
101 union snd_firewire_event event
= {
102 .lock_status
.type
= SNDRV_FIREWIRE_EVENT_LOCK_STATUS
,
105 spin_lock_irq(&efw
->lock
);
107 event
.lock_status
.status
= (efw
->dev_lock_count
> 0);
108 efw
->dev_lock_changed
= false;
110 spin_unlock_irq(&efw
->lock
);
112 count
= min_t(long, count
, sizeof(event
.lock_status
));
114 if (copy_to_user(buf
, &event
, count
))
121 hwdep_read(struct snd_hwdep
*hwdep
, char __user
*buf
, long count
,
124 struct snd_efw
*efw
= hwdep
->private_data
;
126 bool dev_lock_changed
;
129 spin_lock_irq(&efw
->lock
);
131 dev_lock_changed
= efw
->dev_lock_changed
;
132 queued
= efw
->push_ptr
!= efw
->pull_ptr
;
134 while (!dev_lock_changed
&& !queued
) {
135 prepare_to_wait(&efw
->hwdep_wait
, &wait
, TASK_INTERRUPTIBLE
);
136 spin_unlock_irq(&efw
->lock
);
138 finish_wait(&efw
->hwdep_wait
, &wait
);
139 if (signal_pending(current
))
141 spin_lock_irq(&efw
->lock
);
142 dev_lock_changed
= efw
->dev_lock_changed
;
143 queued
= efw
->push_ptr
!= efw
->pull_ptr
;
146 spin_unlock_irq(&efw
->lock
);
148 if (dev_lock_changed
)
149 count
= hwdep_read_locked(efw
, buf
, count
, offset
);
151 count
= hwdep_read_resp_buf(efw
, buf
, count
, offset
);
157 hwdep_write(struct snd_hwdep
*hwdep
, const char __user
*data
, long count
,
160 struct snd_efw
*efw
= hwdep
->private_data
;
164 if (count
< sizeof(struct snd_efw_transaction
) ||
165 SND_EFW_RESPONSE_MAXIMUM_BYTES
< count
)
168 buf
= memdup_user(data
, count
);
172 /* check seqnum is not for kernel-land */
173 seqnum
= be32_to_cpu(((struct snd_efw_transaction
*)buf
)->seqnum
);
174 if (seqnum
> SND_EFW_TRANSACTION_USER_SEQNUM_MAX
) {
179 if (snd_efw_transaction_cmd(efw
->unit
, buf
, count
) < 0)
187 hwdep_poll(struct snd_hwdep
*hwdep
, struct file
*file
, poll_table
*wait
)
189 struct snd_efw
*efw
= hwdep
->private_data
;
192 poll_wait(file
, &efw
->hwdep_wait
, wait
);
194 spin_lock_irq(&efw
->lock
);
195 if (efw
->dev_lock_changed
|| efw
->pull_ptr
!= efw
->push_ptr
)
196 events
= EPOLLIN
| EPOLLRDNORM
;
199 spin_unlock_irq(&efw
->lock
);
201 return events
| EPOLLOUT
;
205 hwdep_get_info(struct snd_efw
*efw
, void __user
*arg
)
207 struct fw_device
*dev
= fw_parent_device(efw
->unit
);
208 struct snd_firewire_get_info info
;
210 memset(&info
, 0, sizeof(info
));
211 info
.type
= SNDRV_FIREWIRE_TYPE_FIREWORKS
;
212 info
.card
= dev
->card
->index
;
213 *(__be32
*)&info
.guid
[0] = cpu_to_be32(dev
->config_rom
[3]);
214 *(__be32
*)&info
.guid
[4] = cpu_to_be32(dev
->config_rom
[4]);
215 strlcpy(info
.device_name
, dev_name(&dev
->device
),
216 sizeof(info
.device_name
));
218 if (copy_to_user(arg
, &info
, sizeof(info
)))
225 hwdep_lock(struct snd_efw
*efw
)
229 spin_lock_irq(&efw
->lock
);
231 if (efw
->dev_lock_count
== 0) {
232 efw
->dev_lock_count
= -1;
238 spin_unlock_irq(&efw
->lock
);
244 hwdep_unlock(struct snd_efw
*efw
)
248 spin_lock_irq(&efw
->lock
);
250 if (efw
->dev_lock_count
== -1) {
251 efw
->dev_lock_count
= 0;
257 spin_unlock_irq(&efw
->lock
);
263 hwdep_release(struct snd_hwdep
*hwdep
, struct file
*file
)
265 struct snd_efw
*efw
= hwdep
->private_data
;
267 spin_lock_irq(&efw
->lock
);
268 if (efw
->dev_lock_count
== -1)
269 efw
->dev_lock_count
= 0;
270 spin_unlock_irq(&efw
->lock
);
276 hwdep_ioctl(struct snd_hwdep
*hwdep
, struct file
*file
,
277 unsigned int cmd
, unsigned long arg
)
279 struct snd_efw
*efw
= hwdep
->private_data
;
282 case SNDRV_FIREWIRE_IOCTL_GET_INFO
:
283 return hwdep_get_info(efw
, (void __user
*)arg
);
284 case SNDRV_FIREWIRE_IOCTL_LOCK
:
285 return hwdep_lock(efw
);
286 case SNDRV_FIREWIRE_IOCTL_UNLOCK
:
287 return hwdep_unlock(efw
);
295 hwdep_compat_ioctl(struct snd_hwdep
*hwdep
, struct file
*file
,
296 unsigned int cmd
, unsigned long arg
)
298 return hwdep_ioctl(hwdep
, file
, cmd
,
299 (unsigned long)compat_ptr(arg
));
302 #define hwdep_compat_ioctl NULL
305 int snd_efw_create_hwdep_device(struct snd_efw
*efw
)
307 static const struct snd_hwdep_ops ops
= {
309 .write
= hwdep_write
,
310 .release
= hwdep_release
,
312 .ioctl
= hwdep_ioctl
,
313 .ioctl_compat
= hwdep_compat_ioctl
,
315 struct snd_hwdep
*hwdep
;
318 err
= snd_hwdep_new(efw
->card
, "Fireworks", 0, &hwdep
);
321 strcpy(hwdep
->name
, "Fireworks");
322 hwdep
->iface
= SNDRV_HWDEP_IFACE_FW_FIREWORKS
;
324 hwdep
->private_data
= efw
;
325 hwdep
->exclusive
= true;