1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * OSS compatible sequencer driver |
4 | * |
5 | * synth device handlers |
6 | * |
7 | * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de> |
8 | */ |
9 | |
10 | #include "seq_oss_synth.h" |
11 | #include "seq_oss_midi.h" |
12 | #include "../seq_lock.h" |
13 | #include <linux/init.h> |
14 | #include <linux/module.h> |
15 | #include <linux/slab.h> |
16 | #include <linux/nospec.h> |
17 | |
18 | /* |
19 | * constants |
20 | */ |
21 | #define SNDRV_SEQ_OSS_MAX_SYNTH_NAME 30 |
22 | #define MAX_SYSEX_BUFLEN 128 |
23 | |
24 | |
25 | /* |
26 | * definition of synth info records |
27 | */ |
28 | |
29 | /* sysex buffer */ |
30 | struct seq_oss_synth_sysex { |
31 | int len; |
32 | int skip; |
33 | unsigned char buf[MAX_SYSEX_BUFLEN]; |
34 | }; |
35 | |
36 | /* synth info */ |
37 | struct seq_oss_synth { |
38 | int seq_device; |
39 | |
40 | /* for synth_info */ |
41 | int synth_type; |
42 | int synth_subtype; |
43 | int nr_voices; |
44 | |
45 | char name[SNDRV_SEQ_OSS_MAX_SYNTH_NAME]; |
46 | struct snd_seq_oss_callback oper; |
47 | |
48 | int opened; |
49 | |
50 | void *private_data; |
51 | snd_use_lock_t use_lock; |
52 | }; |
53 | |
54 | |
55 | /* |
56 | * device table |
57 | */ |
58 | static int max_synth_devs; |
59 | static struct seq_oss_synth *synth_devs[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS]; |
60 | static struct seq_oss_synth midi_synth_dev = { |
61 | .seq_device = -1, |
62 | .synth_type = SYNTH_TYPE_MIDI, |
63 | .synth_subtype = 0, |
64 | .nr_voices = 16, |
65 | .name = "MIDI" , |
66 | }; |
67 | |
68 | static DEFINE_SPINLOCK(register_lock); |
69 | |
70 | /* |
71 | * prototypes |
72 | */ |
73 | static struct seq_oss_synth *get_synthdev(struct seq_oss_devinfo *dp, int dev); |
74 | static void reset_channels(struct seq_oss_synthinfo *info); |
75 | |
76 | /* |
77 | * global initialization |
78 | */ |
79 | void __init |
80 | snd_seq_oss_synth_init(void) |
81 | { |
82 | snd_use_lock_init(&midi_synth_dev.use_lock); |
83 | } |
84 | |
85 | /* |
86 | * registration of the synth device |
87 | */ |
88 | int |
89 | snd_seq_oss_synth_probe(struct device *_dev) |
90 | { |
91 | struct snd_seq_device *dev = to_seq_dev(_dev); |
92 | int i; |
93 | struct seq_oss_synth *rec; |
94 | struct snd_seq_oss_reg *reg = SNDRV_SEQ_DEVICE_ARGPTR(dev); |
95 | unsigned long flags; |
96 | |
97 | rec = kzalloc(size: sizeof(*rec), GFP_KERNEL); |
98 | if (!rec) |
99 | return -ENOMEM; |
100 | rec->seq_device = -1; |
101 | rec->synth_type = reg->type; |
102 | rec->synth_subtype = reg->subtype; |
103 | rec->nr_voices = reg->nvoices; |
104 | rec->oper = reg->oper; |
105 | rec->private_data = reg->private_data; |
106 | rec->opened = 0; |
107 | snd_use_lock_init(&rec->use_lock); |
108 | |
109 | /* copy and truncate the name of synth device */ |
110 | strscpy(rec->name, dev->name, sizeof(rec->name)); |
111 | |
112 | /* registration */ |
113 | spin_lock_irqsave(®ister_lock, flags); |
114 | for (i = 0; i < max_synth_devs; i++) { |
115 | if (synth_devs[i] == NULL) |
116 | break; |
117 | } |
118 | if (i >= max_synth_devs) { |
119 | if (max_synth_devs >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) { |
120 | spin_unlock_irqrestore(lock: ®ister_lock, flags); |
121 | pr_err("ALSA: seq_oss: no more synth slot\n" ); |
122 | kfree(objp: rec); |
123 | return -ENOMEM; |
124 | } |
125 | max_synth_devs++; |
126 | } |
127 | rec->seq_device = i; |
128 | synth_devs[i] = rec; |
129 | spin_unlock_irqrestore(lock: ®ister_lock, flags); |
130 | dev->driver_data = rec; |
131 | #ifdef SNDRV_OSS_INFO_DEV_SYNTH |
132 | if (i < SNDRV_CARDS) |
133 | snd_oss_info_register(SNDRV_OSS_INFO_DEV_SYNTH, num: i, string: rec->name); |
134 | #endif |
135 | return 0; |
136 | } |
137 | |
138 | |
139 | int |
140 | snd_seq_oss_synth_remove(struct device *_dev) |
141 | { |
142 | struct snd_seq_device *dev = to_seq_dev(_dev); |
143 | int index; |
144 | struct seq_oss_synth *rec = dev->driver_data; |
145 | unsigned long flags; |
146 | |
147 | spin_lock_irqsave(®ister_lock, flags); |
148 | for (index = 0; index < max_synth_devs; index++) { |
149 | if (synth_devs[index] == rec) |
150 | break; |
151 | } |
152 | if (index >= max_synth_devs) { |
153 | spin_unlock_irqrestore(lock: ®ister_lock, flags); |
154 | pr_err("ALSA: seq_oss: can't unregister synth\n" ); |
155 | return -EINVAL; |
156 | } |
157 | synth_devs[index] = NULL; |
158 | if (index == max_synth_devs - 1) { |
159 | for (index--; index >= 0; index--) { |
160 | if (synth_devs[index]) |
161 | break; |
162 | } |
163 | max_synth_devs = index + 1; |
164 | } |
165 | spin_unlock_irqrestore(lock: ®ister_lock, flags); |
166 | #ifdef SNDRV_OSS_INFO_DEV_SYNTH |
167 | if (rec->seq_device < SNDRV_CARDS) |
168 | snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_SYNTH, rec->seq_device); |
169 | #endif |
170 | |
171 | snd_use_lock_sync(&rec->use_lock); |
172 | kfree(objp: rec); |
173 | |
174 | return 0; |
175 | } |
176 | |
177 | |
178 | /* |
179 | */ |
180 | static struct seq_oss_synth * |
181 | get_sdev(int dev) |
182 | { |
183 | struct seq_oss_synth *rec; |
184 | unsigned long flags; |
185 | |
186 | spin_lock_irqsave(®ister_lock, flags); |
187 | rec = synth_devs[dev]; |
188 | if (rec) |
189 | snd_use_lock_use(&rec->use_lock); |
190 | spin_unlock_irqrestore(lock: ®ister_lock, flags); |
191 | return rec; |
192 | } |
193 | |
194 | |
195 | /* |
196 | * set up synth tables |
197 | */ |
198 | |
199 | void |
200 | snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp) |
201 | { |
202 | int i; |
203 | struct seq_oss_synth *rec; |
204 | struct seq_oss_synthinfo *info; |
205 | |
206 | dp->max_synthdev = max_synth_devs; |
207 | dp->synth_opened = 0; |
208 | memset(dp->synths, 0, sizeof(dp->synths)); |
209 | for (i = 0; i < dp->max_synthdev; i++) { |
210 | rec = get_sdev(dev: i); |
211 | if (rec == NULL) |
212 | continue; |
213 | if (rec->oper.open == NULL || rec->oper.close == NULL) { |
214 | snd_use_lock_free(&rec->use_lock); |
215 | continue; |
216 | } |
217 | info = &dp->synths[i]; |
218 | info->arg.app_index = dp->port; |
219 | info->arg.file_mode = dp->file_mode; |
220 | info->arg.seq_mode = dp->seq_mode; |
221 | if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) |
222 | info->arg.event_passing = SNDRV_SEQ_OSS_PROCESS_EVENTS; |
223 | else |
224 | info->arg.event_passing = SNDRV_SEQ_OSS_PASS_EVENTS; |
225 | info->opened = 0; |
226 | if (!try_module_get(module: rec->oper.owner)) { |
227 | snd_use_lock_free(&rec->use_lock); |
228 | continue; |
229 | } |
230 | if (rec->oper.open(&info->arg, rec->private_data) < 0) { |
231 | module_put(module: rec->oper.owner); |
232 | snd_use_lock_free(&rec->use_lock); |
233 | continue; |
234 | } |
235 | info->nr_voices = rec->nr_voices; |
236 | if (info->nr_voices > 0) { |
237 | info->ch = kcalloc(n: info->nr_voices, size: sizeof(struct seq_oss_chinfo), GFP_KERNEL); |
238 | if (!info->ch) { |
239 | rec->oper.close(&info->arg); |
240 | module_put(module: rec->oper.owner); |
241 | snd_use_lock_free(&rec->use_lock); |
242 | continue; |
243 | } |
244 | reset_channels(info); |
245 | } |
246 | info->opened++; |
247 | rec->opened++; |
248 | dp->synth_opened++; |
249 | snd_use_lock_free(&rec->use_lock); |
250 | } |
251 | } |
252 | |
253 | |
254 | /* |
255 | * set up synth tables for MIDI emulation - /dev/music mode only |
256 | */ |
257 | |
258 | void |
259 | snd_seq_oss_synth_setup_midi(struct seq_oss_devinfo *dp) |
260 | { |
261 | int i; |
262 | |
263 | if (dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) |
264 | return; |
265 | |
266 | for (i = 0; i < dp->max_mididev; i++) { |
267 | struct seq_oss_synthinfo *info; |
268 | info = &dp->synths[dp->max_synthdev]; |
269 | if (snd_seq_oss_midi_open(dp, dev: i, file_mode: dp->file_mode) < 0) |
270 | continue; |
271 | info->arg.app_index = dp->port; |
272 | info->arg.file_mode = dp->file_mode; |
273 | info->arg.seq_mode = dp->seq_mode; |
274 | info->arg.private_data = info; |
275 | info->is_midi = 1; |
276 | info->midi_mapped = i; |
277 | info->arg.event_passing = SNDRV_SEQ_OSS_PASS_EVENTS; |
278 | snd_seq_oss_midi_get_addr(dp, dev: i, addr: &info->arg.addr); |
279 | info->opened = 1; |
280 | midi_synth_dev.opened++; |
281 | dp->max_synthdev++; |
282 | if (dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) |
283 | break; |
284 | } |
285 | } |
286 | |
287 | |
288 | /* |
289 | * clean up synth tables |
290 | */ |
291 | |
292 | void |
293 | snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp) |
294 | { |
295 | int i; |
296 | struct seq_oss_synth *rec; |
297 | struct seq_oss_synthinfo *info; |
298 | |
299 | if (snd_BUG_ON(dp->max_synthdev > SNDRV_SEQ_OSS_MAX_SYNTH_DEVS)) |
300 | return; |
301 | for (i = 0; i < dp->max_synthdev; i++) { |
302 | info = &dp->synths[i]; |
303 | if (! info->opened) |
304 | continue; |
305 | if (info->is_midi) { |
306 | if (midi_synth_dev.opened > 0) { |
307 | snd_seq_oss_midi_close(dp, dev: info->midi_mapped); |
308 | midi_synth_dev.opened--; |
309 | } |
310 | } else { |
311 | rec = get_sdev(dev: i); |
312 | if (rec == NULL) |
313 | continue; |
314 | if (rec->opened > 0) { |
315 | rec->oper.close(&info->arg); |
316 | module_put(module: rec->oper.owner); |
317 | rec->opened = 0; |
318 | } |
319 | snd_use_lock_free(&rec->use_lock); |
320 | } |
321 | kfree(objp: info->sysex); |
322 | info->sysex = NULL; |
323 | kfree(objp: info->ch); |
324 | info->ch = NULL; |
325 | } |
326 | dp->synth_opened = 0; |
327 | dp->max_synthdev = 0; |
328 | } |
329 | |
330 | static struct seq_oss_synthinfo * |
331 | get_synthinfo_nospec(struct seq_oss_devinfo *dp, int dev) |
332 | { |
333 | if (dev < 0 || dev >= dp->max_synthdev) |
334 | return NULL; |
335 | dev = array_index_nospec(dev, SNDRV_SEQ_OSS_MAX_SYNTH_DEVS); |
336 | return &dp->synths[dev]; |
337 | } |
338 | |
339 | /* |
340 | * return synth device information pointer |
341 | */ |
342 | static struct seq_oss_synth * |
343 | get_synthdev(struct seq_oss_devinfo *dp, int dev) |
344 | { |
345 | struct seq_oss_synth *rec; |
346 | struct seq_oss_synthinfo *info = get_synthinfo_nospec(dp, dev); |
347 | |
348 | if (!info) |
349 | return NULL; |
350 | if (!info->opened) |
351 | return NULL; |
352 | if (info->is_midi) { |
353 | rec = &midi_synth_dev; |
354 | snd_use_lock_use(&rec->use_lock); |
355 | } else { |
356 | rec = get_sdev(dev); |
357 | if (!rec) |
358 | return NULL; |
359 | } |
360 | if (! rec->opened) { |
361 | snd_use_lock_free(&rec->use_lock); |
362 | return NULL; |
363 | } |
364 | return rec; |
365 | } |
366 | |
367 | |
368 | /* |
369 | * reset note and velocity on each channel. |
370 | */ |
371 | static void |
372 | reset_channels(struct seq_oss_synthinfo *info) |
373 | { |
374 | int i; |
375 | if (info->ch == NULL || ! info->nr_voices) |
376 | return; |
377 | for (i = 0; i < info->nr_voices; i++) { |
378 | info->ch[i].note = -1; |
379 | info->ch[i].vel = 0; |
380 | } |
381 | } |
382 | |
383 | |
384 | /* |
385 | * reset synth device: |
386 | * call reset callback. if no callback is defined, send a heartbeat |
387 | * event to the corresponding port. |
388 | */ |
389 | void |
390 | snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev) |
391 | { |
392 | struct seq_oss_synth *rec; |
393 | struct seq_oss_synthinfo *info; |
394 | |
395 | info = get_synthinfo_nospec(dp, dev); |
396 | if (!info || !info->opened) |
397 | return; |
398 | if (info->sysex) |
399 | info->sysex->len = 0; /* reset sysex */ |
400 | reset_channels(info); |
401 | if (info->is_midi) { |
402 | if (midi_synth_dev.opened <= 0) |
403 | return; |
404 | snd_seq_oss_midi_reset(dp, dev: info->midi_mapped); |
405 | /* reopen the device */ |
406 | snd_seq_oss_midi_close(dp, dev); |
407 | if (snd_seq_oss_midi_open(dp, dev: info->midi_mapped, |
408 | file_mode: dp->file_mode) < 0) { |
409 | midi_synth_dev.opened--; |
410 | info->opened = 0; |
411 | kfree(objp: info->sysex); |
412 | info->sysex = NULL; |
413 | kfree(objp: info->ch); |
414 | info->ch = NULL; |
415 | } |
416 | return; |
417 | } |
418 | |
419 | rec = get_sdev(dev); |
420 | if (rec == NULL) |
421 | return; |
422 | if (rec->oper.reset) { |
423 | rec->oper.reset(&info->arg); |
424 | } else { |
425 | struct snd_seq_event ev; |
426 | memset(&ev, 0, sizeof(ev)); |
427 | snd_seq_oss_fill_addr(dp, ev: &ev, dest_client: info->arg.addr.client, |
428 | dest_port: info->arg.addr.port); |
429 | ev.type = SNDRV_SEQ_EVENT_RESET; |
430 | snd_seq_oss_dispatch(dp, ev: &ev, atomic: 0, hop: 0); |
431 | } |
432 | snd_use_lock_free(&rec->use_lock); |
433 | } |
434 | |
435 | |
436 | /* |
437 | * load a patch record: |
438 | * call load_patch callback function |
439 | */ |
440 | int |
441 | snd_seq_oss_synth_load_patch(struct seq_oss_devinfo *dp, int dev, int fmt, |
442 | const char __user *buf, int p, int c) |
443 | { |
444 | struct seq_oss_synth *rec; |
445 | struct seq_oss_synthinfo *info; |
446 | int rc; |
447 | |
448 | info = get_synthinfo_nospec(dp, dev); |
449 | if (!info) |
450 | return -ENXIO; |
451 | |
452 | if (info->is_midi) |
453 | return 0; |
454 | rec = get_synthdev(dp, dev); |
455 | if (!rec) |
456 | return -ENXIO; |
457 | |
458 | if (rec->oper.load_patch == NULL) |
459 | rc = -ENXIO; |
460 | else |
461 | rc = rec->oper.load_patch(&info->arg, fmt, buf, p, c); |
462 | snd_use_lock_free(&rec->use_lock); |
463 | return rc; |
464 | } |
465 | |
466 | /* |
467 | * check if the device is valid synth device and return the synth info |
468 | */ |
469 | struct seq_oss_synthinfo * |
470 | snd_seq_oss_synth_info(struct seq_oss_devinfo *dp, int dev) |
471 | { |
472 | struct seq_oss_synth *rec; |
473 | |
474 | rec = get_synthdev(dp, dev); |
475 | if (rec) { |
476 | snd_use_lock_free(&rec->use_lock); |
477 | return get_synthinfo_nospec(dp, dev); |
478 | } |
479 | return NULL; |
480 | } |
481 | |
482 | |
483 | /* |
484 | * receive OSS 6 byte sysex packet: |
485 | * the full sysex message will be sent if it reaches to the end of data |
486 | * (0xff). |
487 | */ |
488 | int |
489 | snd_seq_oss_synth_sysex(struct seq_oss_devinfo *dp, int dev, unsigned char *buf, struct snd_seq_event *ev) |
490 | { |
491 | int i, send; |
492 | unsigned char *dest; |
493 | struct seq_oss_synth_sysex *sysex; |
494 | struct seq_oss_synthinfo *info; |
495 | |
496 | info = snd_seq_oss_synth_info(dp, dev); |
497 | if (!info) |
498 | return -ENXIO; |
499 | |
500 | sysex = info->sysex; |
501 | if (sysex == NULL) { |
502 | sysex = kzalloc(size: sizeof(*sysex), GFP_KERNEL); |
503 | if (sysex == NULL) |
504 | return -ENOMEM; |
505 | info->sysex = sysex; |
506 | } |
507 | |
508 | send = 0; |
509 | dest = sysex->buf + sysex->len; |
510 | /* copy 6 byte packet to the buffer */ |
511 | for (i = 0; i < 6; i++) { |
512 | if (buf[i] == 0xff) { |
513 | send = 1; |
514 | break; |
515 | } |
516 | dest[i] = buf[i]; |
517 | sysex->len++; |
518 | if (sysex->len >= MAX_SYSEX_BUFLEN) { |
519 | sysex->len = 0; |
520 | sysex->skip = 1; |
521 | break; |
522 | } |
523 | } |
524 | |
525 | if (sysex->len && send) { |
526 | if (sysex->skip) { |
527 | sysex->skip = 0; |
528 | sysex->len = 0; |
529 | return -EINVAL; /* skip */ |
530 | } |
531 | /* copy the data to event record and send it */ |
532 | ev->flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE; |
533 | if (snd_seq_oss_synth_addr(dp, dev, ev)) |
534 | return -EINVAL; |
535 | ev->data.ext.len = sysex->len; |
536 | ev->data.ext.ptr = sysex->buf; |
537 | sysex->len = 0; |
538 | return 0; |
539 | } |
540 | |
541 | return -EINVAL; /* skip */ |
542 | } |
543 | |
544 | /* |
545 | * fill the event source/destination addresses |
546 | */ |
547 | int |
548 | snd_seq_oss_synth_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_event *ev) |
549 | { |
550 | struct seq_oss_synthinfo *info = snd_seq_oss_synth_info(dp, dev); |
551 | |
552 | if (!info) |
553 | return -EINVAL; |
554 | snd_seq_oss_fill_addr(dp, ev, dest_client: info->arg.addr.client, |
555 | dest_port: info->arg.addr.port); |
556 | return 0; |
557 | } |
558 | |
559 | |
560 | /* |
561 | * OSS compatible ioctl |
562 | */ |
563 | int |
564 | snd_seq_oss_synth_ioctl(struct seq_oss_devinfo *dp, int dev, unsigned int cmd, unsigned long addr) |
565 | { |
566 | struct seq_oss_synth *rec; |
567 | struct seq_oss_synthinfo *info; |
568 | int rc; |
569 | |
570 | info = get_synthinfo_nospec(dp, dev); |
571 | if (!info || info->is_midi) |
572 | return -ENXIO; |
573 | rec = get_synthdev(dp, dev); |
574 | if (!rec) |
575 | return -ENXIO; |
576 | if (rec->oper.ioctl == NULL) |
577 | rc = -ENXIO; |
578 | else |
579 | rc = rec->oper.ioctl(&info->arg, cmd, addr); |
580 | snd_use_lock_free(&rec->use_lock); |
581 | return rc; |
582 | } |
583 | |
584 | |
585 | /* |
586 | * send OSS raw events - SEQ_PRIVATE and SEQ_VOLUME |
587 | */ |
588 | int |
589 | snd_seq_oss_synth_raw_event(struct seq_oss_devinfo *dp, int dev, unsigned char *data, struct snd_seq_event *ev) |
590 | { |
591 | struct seq_oss_synthinfo *info; |
592 | |
593 | info = snd_seq_oss_synth_info(dp, dev); |
594 | if (!info || info->is_midi) |
595 | return -ENXIO; |
596 | ev->type = SNDRV_SEQ_EVENT_OSS; |
597 | memcpy(ev->data.raw8.d, data, 8); |
598 | return snd_seq_oss_synth_addr(dp, dev, ev); |
599 | } |
600 | |
601 | |
602 | /* |
603 | * create OSS compatible synth_info record |
604 | */ |
605 | int |
606 | snd_seq_oss_synth_make_info(struct seq_oss_devinfo *dp, int dev, struct synth_info *inf) |
607 | { |
608 | struct seq_oss_synth *rec; |
609 | struct seq_oss_synthinfo *info = get_synthinfo_nospec(dp, dev); |
610 | |
611 | if (!info) |
612 | return -ENXIO; |
613 | |
614 | if (info->is_midi) { |
615 | struct midi_info minf; |
616 | if (snd_seq_oss_midi_make_info(dp, dev: info->midi_mapped, inf: &minf)) |
617 | return -ENXIO; |
618 | inf->synth_type = SYNTH_TYPE_MIDI; |
619 | inf->synth_subtype = 0; |
620 | inf->nr_voices = 16; |
621 | inf->device = dev; |
622 | strscpy(inf->name, minf.name, sizeof(inf->name)); |
623 | } else { |
624 | rec = get_synthdev(dp, dev); |
625 | if (!rec) |
626 | return -ENXIO; |
627 | inf->synth_type = rec->synth_type; |
628 | inf->synth_subtype = rec->synth_subtype; |
629 | inf->nr_voices = rec->nr_voices; |
630 | inf->device = dev; |
631 | strscpy(inf->name, rec->name, sizeof(inf->name)); |
632 | snd_use_lock_free(&rec->use_lock); |
633 | } |
634 | return 0; |
635 | } |
636 | |
637 | |
638 | #ifdef CONFIG_SND_PROC_FS |
639 | /* |
640 | * proc interface |
641 | */ |
642 | void |
643 | snd_seq_oss_synth_info_read(struct snd_info_buffer *buf) |
644 | { |
645 | int i; |
646 | struct seq_oss_synth *rec; |
647 | |
648 | snd_iprintf(buf, "\nNumber of synth devices: %d\n" , max_synth_devs); |
649 | for (i = 0; i < max_synth_devs; i++) { |
650 | snd_iprintf(buf, "\nsynth %d: " , i); |
651 | rec = get_sdev(dev: i); |
652 | if (rec == NULL) { |
653 | snd_iprintf(buf, "*empty*\n" ); |
654 | continue; |
655 | } |
656 | snd_iprintf(buf, "[%s]\n" , rec->name); |
657 | snd_iprintf(buf, " type 0x%x : subtype 0x%x : voices %d\n" , |
658 | rec->synth_type, rec->synth_subtype, |
659 | rec->nr_voices); |
660 | snd_iprintf(buf, " capabilities : ioctl %s / load_patch %s\n" , |
661 | enabled_str((long)rec->oper.ioctl), |
662 | enabled_str((long)rec->oper.load_patch)); |
663 | snd_use_lock_free(&rec->use_lock); |
664 | } |
665 | } |
666 | #endif /* CONFIG_SND_PROC_FS */ |
667 | |