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