1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * OSS compatible sequencer driver |
4 | * |
5 | * registration of device and proc |
6 | * |
7 | * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de> |
8 | */ |
9 | |
10 | #include <linux/init.h> |
11 | #include <linux/module.h> |
12 | #include <linux/mutex.h> |
13 | #include <linux/compat.h> |
14 | #include <sound/core.h> |
15 | #include <sound/minors.h> |
16 | #include <sound/initval.h> |
17 | #include "seq_oss_device.h" |
18 | #include "seq_oss_synth.h" |
19 | |
20 | /* |
21 | * module option |
22 | */ |
23 | MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>" ); |
24 | MODULE_DESCRIPTION("OSS-compatible sequencer module" ); |
25 | MODULE_LICENSE("GPL" ); |
26 | /* Takashi says this is really only for sound-service-0-, but this is OK. */ |
27 | MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_SEQUENCER); |
28 | MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_MUSIC); |
29 | |
30 | |
31 | /* |
32 | * prototypes |
33 | */ |
34 | static int register_device(void); |
35 | static void unregister_device(void); |
36 | #ifdef CONFIG_SND_PROC_FS |
37 | static int register_proc(void); |
38 | static void unregister_proc(void); |
39 | #else |
40 | static inline int register_proc(void) { return 0; } |
41 | static inline void unregister_proc(void) {} |
42 | #endif |
43 | |
44 | static int odev_open(struct inode *inode, struct file *file); |
45 | static int odev_release(struct inode *inode, struct file *file); |
46 | static ssize_t odev_read(struct file *file, char __user *buf, size_t count, loff_t *offset); |
47 | static ssize_t odev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset); |
48 | static long odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg); |
49 | static __poll_t odev_poll(struct file *file, poll_table * wait); |
50 | |
51 | |
52 | /* |
53 | * module interface |
54 | */ |
55 | |
56 | static struct snd_seq_driver seq_oss_synth_driver = { |
57 | .driver = { |
58 | .name = KBUILD_MODNAME, |
59 | .probe = snd_seq_oss_synth_probe, |
60 | .remove = snd_seq_oss_synth_remove, |
61 | }, |
62 | .id = SNDRV_SEQ_DEV_ID_OSS, |
63 | .argsize = sizeof(struct snd_seq_oss_reg), |
64 | }; |
65 | |
66 | static int __init alsa_seq_oss_init(void) |
67 | { |
68 | int rc; |
69 | |
70 | rc = register_device(); |
71 | if (rc < 0) |
72 | goto error; |
73 | rc = register_proc(); |
74 | if (rc < 0) { |
75 | unregister_device(); |
76 | goto error; |
77 | } |
78 | rc = snd_seq_oss_create_client(); |
79 | if (rc < 0) { |
80 | unregister_proc(); |
81 | unregister_device(); |
82 | goto error; |
83 | } |
84 | |
85 | rc = snd_seq_driver_register(&seq_oss_synth_driver); |
86 | if (rc < 0) { |
87 | snd_seq_oss_delete_client(); |
88 | unregister_proc(); |
89 | unregister_device(); |
90 | goto error; |
91 | } |
92 | |
93 | /* success */ |
94 | snd_seq_oss_synth_init(); |
95 | |
96 | error: |
97 | return rc; |
98 | } |
99 | |
100 | static void __exit alsa_seq_oss_exit(void) |
101 | { |
102 | snd_seq_driver_unregister(drv: &seq_oss_synth_driver); |
103 | snd_seq_oss_delete_client(); |
104 | unregister_proc(); |
105 | unregister_device(); |
106 | } |
107 | |
108 | module_init(alsa_seq_oss_init) |
109 | module_exit(alsa_seq_oss_exit) |
110 | |
111 | /* |
112 | * ALSA minor device interface |
113 | */ |
114 | |
115 | static DEFINE_MUTEX(register_mutex); |
116 | |
117 | static int |
118 | odev_open(struct inode *inode, struct file *file) |
119 | { |
120 | int level, rc; |
121 | |
122 | if (iminor(inode) == SNDRV_MINOR_OSS_MUSIC) |
123 | level = SNDRV_SEQ_OSS_MODE_MUSIC; |
124 | else |
125 | level = SNDRV_SEQ_OSS_MODE_SYNTH; |
126 | |
127 | mutex_lock(®ister_mutex); |
128 | rc = snd_seq_oss_open(file, level); |
129 | mutex_unlock(lock: ®ister_mutex); |
130 | |
131 | return rc; |
132 | } |
133 | |
134 | static int |
135 | odev_release(struct inode *inode, struct file *file) |
136 | { |
137 | struct seq_oss_devinfo *dp; |
138 | |
139 | dp = file->private_data; |
140 | if (!dp) |
141 | return 0; |
142 | |
143 | mutex_lock(®ister_mutex); |
144 | snd_seq_oss_release(dp); |
145 | mutex_unlock(lock: ®ister_mutex); |
146 | |
147 | return 0; |
148 | } |
149 | |
150 | static ssize_t |
151 | odev_read(struct file *file, char __user *buf, size_t count, loff_t *offset) |
152 | { |
153 | struct seq_oss_devinfo *dp; |
154 | dp = file->private_data; |
155 | if (snd_BUG_ON(!dp)) |
156 | return -ENXIO; |
157 | return snd_seq_oss_read(dev: dp, buf, count); |
158 | } |
159 | |
160 | |
161 | static ssize_t |
162 | odev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) |
163 | { |
164 | struct seq_oss_devinfo *dp; |
165 | dp = file->private_data; |
166 | if (snd_BUG_ON(!dp)) |
167 | return -ENXIO; |
168 | return snd_seq_oss_write(dp, buf, count, opt: file); |
169 | } |
170 | |
171 | static long |
172 | odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
173 | { |
174 | struct seq_oss_devinfo *dp; |
175 | long rc; |
176 | |
177 | dp = file->private_data; |
178 | if (snd_BUG_ON(!dp)) |
179 | return -ENXIO; |
180 | |
181 | if (cmd != SNDCTL_SEQ_SYNC && |
182 | mutex_lock_interruptible(®ister_mutex)) |
183 | return -ERESTARTSYS; |
184 | rc = snd_seq_oss_ioctl(dp, cmd, arg); |
185 | if (cmd != SNDCTL_SEQ_SYNC) |
186 | mutex_unlock(lock: ®ister_mutex); |
187 | return rc; |
188 | } |
189 | |
190 | #ifdef CONFIG_COMPAT |
191 | static long odev_ioctl_compat(struct file *file, unsigned int cmd, |
192 | unsigned long arg) |
193 | { |
194 | return odev_ioctl(file, cmd, arg: (unsigned long)compat_ptr(uptr: arg)); |
195 | } |
196 | #else |
197 | #define odev_ioctl_compat NULL |
198 | #endif |
199 | |
200 | static __poll_t |
201 | odev_poll(struct file *file, poll_table * wait) |
202 | { |
203 | struct seq_oss_devinfo *dp; |
204 | dp = file->private_data; |
205 | if (snd_BUG_ON(!dp)) |
206 | return EPOLLERR; |
207 | return snd_seq_oss_poll(dp, file, wait); |
208 | } |
209 | |
210 | /* |
211 | * registration of sequencer minor device |
212 | */ |
213 | |
214 | static const struct file_operations seq_oss_f_ops = |
215 | { |
216 | .owner = THIS_MODULE, |
217 | .read = odev_read, |
218 | .write = odev_write, |
219 | .open = odev_open, |
220 | .release = odev_release, |
221 | .poll = odev_poll, |
222 | .unlocked_ioctl = odev_ioctl, |
223 | .compat_ioctl = odev_ioctl_compat, |
224 | .llseek = noop_llseek, |
225 | }; |
226 | |
227 | static int __init |
228 | register_device(void) |
229 | { |
230 | int rc; |
231 | |
232 | mutex_lock(®ister_mutex); |
233 | rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, |
234 | NULL, dev: 0, |
235 | f_ops: &seq_oss_f_ops, NULL); |
236 | if (rc < 0) { |
237 | pr_err("ALSA: seq_oss: can't register device seq\n" ); |
238 | mutex_unlock(lock: ®ister_mutex); |
239 | return rc; |
240 | } |
241 | rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC, |
242 | NULL, dev: 0, |
243 | f_ops: &seq_oss_f_ops, NULL); |
244 | if (rc < 0) { |
245 | pr_err("ALSA: seq_oss: can't register device music\n" ); |
246 | snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, dev: 0); |
247 | mutex_unlock(lock: ®ister_mutex); |
248 | return rc; |
249 | } |
250 | mutex_unlock(lock: ®ister_mutex); |
251 | return 0; |
252 | } |
253 | |
254 | static void |
255 | unregister_device(void) |
256 | { |
257 | mutex_lock(®ister_mutex); |
258 | if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC, NULL, dev: 0) < 0) |
259 | pr_err("ALSA: seq_oss: error unregister device music\n" ); |
260 | if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, dev: 0) < 0) |
261 | pr_err("ALSA: seq_oss: error unregister device seq\n" ); |
262 | mutex_unlock(lock: ®ister_mutex); |
263 | } |
264 | |
265 | /* |
266 | * /proc interface |
267 | */ |
268 | |
269 | #ifdef CONFIG_SND_PROC_FS |
270 | |
271 | static struct snd_info_entry *info_entry; |
272 | |
273 | static void |
274 | info_read(struct snd_info_entry *entry, struct snd_info_buffer *buf) |
275 | { |
276 | mutex_lock(®ister_mutex); |
277 | snd_iprintf(buf, "OSS sequencer emulation version %s\n" , SNDRV_SEQ_OSS_VERSION_STR); |
278 | snd_seq_oss_system_info_read(buf); |
279 | snd_seq_oss_synth_info_read(buf); |
280 | snd_seq_oss_midi_info_read(buf); |
281 | mutex_unlock(lock: ®ister_mutex); |
282 | } |
283 | |
284 | |
285 | static int __init |
286 | register_proc(void) |
287 | { |
288 | struct snd_info_entry *entry; |
289 | |
290 | entry = snd_info_create_module_entry(THIS_MODULE, SNDRV_SEQ_OSS_PROCNAME, parent: snd_seq_root); |
291 | if (entry == NULL) |
292 | return -ENOMEM; |
293 | |
294 | entry->content = SNDRV_INFO_CONTENT_TEXT; |
295 | entry->private_data = NULL; |
296 | entry->c.text.read = info_read; |
297 | if (snd_info_register(entry) < 0) { |
298 | snd_info_free_entry(entry); |
299 | return -ENOMEM; |
300 | } |
301 | info_entry = entry; |
302 | return 0; |
303 | } |
304 | |
305 | static void |
306 | unregister_proc(void) |
307 | { |
308 | snd_info_free_entry(entry: info_entry); |
309 | info_entry = NULL; |
310 | } |
311 | #endif /* CONFIG_SND_PROC_FS */ |
312 | |