1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * OSS compatible sequencer driver |
4 | * |
5 | * MIDI device handlers |
6 | * |
7 | * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de> |
8 | */ |
9 | |
10 | #include <sound/asoundef.h> |
11 | #include "seq_oss_midi.h" |
12 | #include "seq_oss_readq.h" |
13 | #include "seq_oss_timer.h" |
14 | #include "seq_oss_event.h" |
15 | #include <sound/seq_midi_event.h> |
16 | #include "../seq_lock.h" |
17 | #include <linux/init.h> |
18 | #include <linux/slab.h> |
19 | #include <linux/nospec.h> |
20 | |
21 | |
22 | /* |
23 | * constants |
24 | */ |
25 | #define SNDRV_SEQ_OSS_MAX_MIDI_NAME 30 |
26 | |
27 | /* |
28 | * definition of midi device record |
29 | */ |
30 | struct seq_oss_midi { |
31 | int seq_device; /* device number */ |
32 | int client; /* sequencer client number */ |
33 | int port; /* sequencer port number */ |
34 | unsigned int flags; /* port capability */ |
35 | int opened; /* flag for opening */ |
36 | unsigned char name[SNDRV_SEQ_OSS_MAX_MIDI_NAME]; |
37 | struct snd_midi_event *coder; /* MIDI event coder */ |
38 | struct seq_oss_devinfo *devinfo; /* assigned OSSseq device */ |
39 | snd_use_lock_t use_lock; |
40 | struct mutex open_mutex; |
41 | }; |
42 | |
43 | |
44 | /* |
45 | * midi device table |
46 | */ |
47 | static int max_midi_devs; |
48 | static struct seq_oss_midi *midi_devs[SNDRV_SEQ_OSS_MAX_MIDI_DEVS]; |
49 | |
50 | static DEFINE_SPINLOCK(register_lock); |
51 | |
52 | /* |
53 | * prototypes |
54 | */ |
55 | static struct seq_oss_midi *get_mdev(int dev); |
56 | static struct seq_oss_midi *get_mididev(struct seq_oss_devinfo *dp, int dev); |
57 | static int send_synth_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, int dev); |
58 | static int send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, struct seq_oss_midi *mdev); |
59 | |
60 | /* |
61 | * look up the existing ports |
62 | * this looks a very exhausting job. |
63 | */ |
64 | int |
65 | snd_seq_oss_midi_lookup_ports(int client) |
66 | { |
67 | struct snd_seq_client_info *clinfo __free(kfree) = NULL; |
68 | struct snd_seq_port_info *pinfo __free(kfree) = NULL; |
69 | |
70 | clinfo = kzalloc(size: sizeof(*clinfo), GFP_KERNEL); |
71 | pinfo = kzalloc(size: sizeof(*pinfo), GFP_KERNEL); |
72 | if (!clinfo || !pinfo) |
73 | return -ENOMEM; |
74 | clinfo->client = -1; |
75 | while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, arg: clinfo) == 0) { |
76 | if (clinfo->client == client) |
77 | continue; /* ignore myself */ |
78 | pinfo->addr.client = clinfo->client; |
79 | pinfo->addr.port = -1; |
80 | while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, arg: pinfo) == 0) |
81 | snd_seq_oss_midi_check_new_port(pinfo); |
82 | } |
83 | return 0; |
84 | } |
85 | |
86 | |
87 | /* |
88 | */ |
89 | static struct seq_oss_midi * |
90 | get_mdev(int dev) |
91 | { |
92 | struct seq_oss_midi *mdev; |
93 | unsigned long flags; |
94 | |
95 | spin_lock_irqsave(®ister_lock, flags); |
96 | mdev = midi_devs[dev]; |
97 | if (mdev) |
98 | snd_use_lock_use(&mdev->use_lock); |
99 | spin_unlock_irqrestore(lock: ®ister_lock, flags); |
100 | return mdev; |
101 | } |
102 | |
103 | /* |
104 | * look for the identical slot |
105 | */ |
106 | static struct seq_oss_midi * |
107 | find_slot(int client, int port) |
108 | { |
109 | int i; |
110 | struct seq_oss_midi *mdev; |
111 | unsigned long flags; |
112 | |
113 | spin_lock_irqsave(®ister_lock, flags); |
114 | for (i = 0; i < max_midi_devs; i++) { |
115 | mdev = midi_devs[i]; |
116 | if (mdev && mdev->client == client && mdev->port == port) { |
117 | /* found! */ |
118 | snd_use_lock_use(&mdev->use_lock); |
119 | spin_unlock_irqrestore(lock: ®ister_lock, flags); |
120 | return mdev; |
121 | } |
122 | } |
123 | spin_unlock_irqrestore(lock: ®ister_lock, flags); |
124 | return NULL; |
125 | } |
126 | |
127 | |
128 | #define PERM_WRITE (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE) |
129 | #define PERM_READ (SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ) |
130 | /* |
131 | * register a new port if it doesn't exist yet |
132 | */ |
133 | int |
134 | snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo) |
135 | { |
136 | int i; |
137 | struct seq_oss_midi *mdev; |
138 | unsigned long flags; |
139 | |
140 | /* the port must include generic midi */ |
141 | if (! (pinfo->type & SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC)) |
142 | return 0; |
143 | /* either read or write subscribable */ |
144 | if ((pinfo->capability & PERM_WRITE) != PERM_WRITE && |
145 | (pinfo->capability & PERM_READ) != PERM_READ) |
146 | return 0; |
147 | |
148 | /* |
149 | * look for the identical slot |
150 | */ |
151 | mdev = find_slot(client: pinfo->addr.client, port: pinfo->addr.port); |
152 | if (mdev) { |
153 | /* already exists */ |
154 | snd_use_lock_free(&mdev->use_lock); |
155 | return 0; |
156 | } |
157 | |
158 | /* |
159 | * allocate midi info record |
160 | */ |
161 | mdev = kzalloc(size: sizeof(*mdev), GFP_KERNEL); |
162 | if (!mdev) |
163 | return -ENOMEM; |
164 | |
165 | /* copy the port information */ |
166 | mdev->client = pinfo->addr.client; |
167 | mdev->port = pinfo->addr.port; |
168 | mdev->flags = pinfo->capability; |
169 | mdev->opened = 0; |
170 | snd_use_lock_init(&mdev->use_lock); |
171 | mutex_init(&mdev->open_mutex); |
172 | |
173 | /* copy and truncate the name of synth device */ |
174 | strscpy(mdev->name, pinfo->name, sizeof(mdev->name)); |
175 | |
176 | /* create MIDI coder */ |
177 | if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, rdev: &mdev->coder) < 0) { |
178 | pr_err("ALSA: seq_oss: can't malloc midi coder\n" ); |
179 | kfree(objp: mdev); |
180 | return -ENOMEM; |
181 | } |
182 | /* OSS sequencer adds running status to all sequences */ |
183 | snd_midi_event_no_status(dev: mdev->coder, on: 1); |
184 | |
185 | /* |
186 | * look for en empty slot |
187 | */ |
188 | spin_lock_irqsave(®ister_lock, flags); |
189 | for (i = 0; i < max_midi_devs; i++) { |
190 | if (midi_devs[i] == NULL) |
191 | break; |
192 | } |
193 | if (i >= max_midi_devs) { |
194 | if (max_midi_devs >= SNDRV_SEQ_OSS_MAX_MIDI_DEVS) { |
195 | spin_unlock_irqrestore(lock: ®ister_lock, flags); |
196 | snd_midi_event_free(dev: mdev->coder); |
197 | kfree(objp: mdev); |
198 | return -ENOMEM; |
199 | } |
200 | max_midi_devs++; |
201 | } |
202 | mdev->seq_device = i; |
203 | midi_devs[mdev->seq_device] = mdev; |
204 | spin_unlock_irqrestore(lock: ®ister_lock, flags); |
205 | |
206 | return 0; |
207 | } |
208 | |
209 | /* |
210 | * release the midi device if it was registered |
211 | */ |
212 | int |
213 | snd_seq_oss_midi_check_exit_port(int client, int port) |
214 | { |
215 | struct seq_oss_midi *mdev; |
216 | unsigned long flags; |
217 | int index; |
218 | |
219 | mdev = find_slot(client, port); |
220 | if (mdev) { |
221 | spin_lock_irqsave(®ister_lock, flags); |
222 | midi_devs[mdev->seq_device] = NULL; |
223 | spin_unlock_irqrestore(lock: ®ister_lock, flags); |
224 | snd_use_lock_free(&mdev->use_lock); |
225 | snd_use_lock_sync(&mdev->use_lock); |
226 | snd_midi_event_free(dev: mdev->coder); |
227 | kfree(objp: mdev); |
228 | } |
229 | spin_lock_irqsave(®ister_lock, flags); |
230 | for (index = max_midi_devs - 1; index >= 0; index--) { |
231 | if (midi_devs[index]) |
232 | break; |
233 | } |
234 | max_midi_devs = index + 1; |
235 | spin_unlock_irqrestore(lock: ®ister_lock, flags); |
236 | return 0; |
237 | } |
238 | |
239 | |
240 | /* |
241 | * release the midi device if it was registered |
242 | */ |
243 | void |
244 | snd_seq_oss_midi_clear_all(void) |
245 | { |
246 | int i; |
247 | struct seq_oss_midi *mdev; |
248 | unsigned long flags; |
249 | |
250 | spin_lock_irqsave(®ister_lock, flags); |
251 | for (i = 0; i < max_midi_devs; i++) { |
252 | mdev = midi_devs[i]; |
253 | if (mdev) { |
254 | snd_midi_event_free(dev: mdev->coder); |
255 | kfree(objp: mdev); |
256 | midi_devs[i] = NULL; |
257 | } |
258 | } |
259 | max_midi_devs = 0; |
260 | spin_unlock_irqrestore(lock: ®ister_lock, flags); |
261 | } |
262 | |
263 | |
264 | /* |
265 | * set up midi tables |
266 | */ |
267 | void |
268 | snd_seq_oss_midi_setup(struct seq_oss_devinfo *dp) |
269 | { |
270 | spin_lock_irq(lock: ®ister_lock); |
271 | dp->max_mididev = max_midi_devs; |
272 | spin_unlock_irq(lock: ®ister_lock); |
273 | } |
274 | |
275 | /* |
276 | * clean up midi tables |
277 | */ |
278 | void |
279 | snd_seq_oss_midi_cleanup(struct seq_oss_devinfo *dp) |
280 | { |
281 | int i; |
282 | for (i = 0; i < dp->max_mididev; i++) |
283 | snd_seq_oss_midi_close(dp, dev: i); |
284 | dp->max_mididev = 0; |
285 | } |
286 | |
287 | |
288 | /* |
289 | * open all midi devices. ignore errors. |
290 | */ |
291 | void |
292 | snd_seq_oss_midi_open_all(struct seq_oss_devinfo *dp, int file_mode) |
293 | { |
294 | int i; |
295 | for (i = 0; i < dp->max_mididev; i++) |
296 | snd_seq_oss_midi_open(dp, dev: i, file_mode); |
297 | } |
298 | |
299 | |
300 | /* |
301 | * get the midi device information |
302 | */ |
303 | static struct seq_oss_midi * |
304 | get_mididev(struct seq_oss_devinfo *dp, int dev) |
305 | { |
306 | if (dev < 0 || dev >= dp->max_mididev) |
307 | return NULL; |
308 | dev = array_index_nospec(dev, dp->max_mididev); |
309 | return get_mdev(dev); |
310 | } |
311 | |
312 | |
313 | /* |
314 | * open the midi device if not opened yet |
315 | */ |
316 | int |
317 | snd_seq_oss_midi_open(struct seq_oss_devinfo *dp, int dev, int fmode) |
318 | { |
319 | int perm; |
320 | struct seq_oss_midi *mdev; |
321 | struct snd_seq_port_subscribe subs; |
322 | int err; |
323 | |
324 | mdev = get_mididev(dp, dev); |
325 | if (!mdev) |
326 | return -ENODEV; |
327 | |
328 | mutex_lock(&mdev->open_mutex); |
329 | /* already used? */ |
330 | if (mdev->opened && mdev->devinfo != dp) { |
331 | err = -EBUSY; |
332 | goto unlock; |
333 | } |
334 | |
335 | perm = 0; |
336 | if (is_write_mode(fmode)) |
337 | perm |= PERM_WRITE; |
338 | if (is_read_mode(fmode)) |
339 | perm |= PERM_READ; |
340 | perm &= mdev->flags; |
341 | if (perm == 0) { |
342 | err = -ENXIO; |
343 | goto unlock; |
344 | } |
345 | |
346 | /* already opened? */ |
347 | if ((mdev->opened & perm) == perm) { |
348 | err = 0; |
349 | goto unlock; |
350 | } |
351 | |
352 | perm &= ~mdev->opened; |
353 | |
354 | memset(&subs, 0, sizeof(subs)); |
355 | |
356 | if (perm & PERM_WRITE) { |
357 | subs.sender = dp->addr; |
358 | subs.dest.client = mdev->client; |
359 | subs.dest.port = mdev->port; |
360 | if (snd_seq_kernel_client_ctl(client: dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, arg: &subs) >= 0) |
361 | mdev->opened |= PERM_WRITE; |
362 | } |
363 | if (perm & PERM_READ) { |
364 | subs.sender.client = mdev->client; |
365 | subs.sender.port = mdev->port; |
366 | subs.dest = dp->addr; |
367 | subs.flags = SNDRV_SEQ_PORT_SUBS_TIMESTAMP; |
368 | subs.queue = dp->queue; /* queue for timestamps */ |
369 | if (snd_seq_kernel_client_ctl(client: dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, arg: &subs) >= 0) |
370 | mdev->opened |= PERM_READ; |
371 | } |
372 | |
373 | if (! mdev->opened) { |
374 | err = -ENXIO; |
375 | goto unlock; |
376 | } |
377 | |
378 | mdev->devinfo = dp; |
379 | err = 0; |
380 | |
381 | unlock: |
382 | mutex_unlock(lock: &mdev->open_mutex); |
383 | snd_use_lock_free(&mdev->use_lock); |
384 | return err; |
385 | } |
386 | |
387 | /* |
388 | * close the midi device if already opened |
389 | */ |
390 | int |
391 | snd_seq_oss_midi_close(struct seq_oss_devinfo *dp, int dev) |
392 | { |
393 | struct seq_oss_midi *mdev; |
394 | struct snd_seq_port_subscribe subs; |
395 | |
396 | mdev = get_mididev(dp, dev); |
397 | if (!mdev) |
398 | return -ENODEV; |
399 | mutex_lock(&mdev->open_mutex); |
400 | if (!mdev->opened || mdev->devinfo != dp) |
401 | goto unlock; |
402 | |
403 | memset(&subs, 0, sizeof(subs)); |
404 | if (mdev->opened & PERM_WRITE) { |
405 | subs.sender = dp->addr; |
406 | subs.dest.client = mdev->client; |
407 | subs.dest.port = mdev->port; |
408 | snd_seq_kernel_client_ctl(client: dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, arg: &subs); |
409 | } |
410 | if (mdev->opened & PERM_READ) { |
411 | subs.sender.client = mdev->client; |
412 | subs.sender.port = mdev->port; |
413 | subs.dest = dp->addr; |
414 | snd_seq_kernel_client_ctl(client: dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, arg: &subs); |
415 | } |
416 | |
417 | mdev->opened = 0; |
418 | mdev->devinfo = NULL; |
419 | |
420 | unlock: |
421 | mutex_unlock(lock: &mdev->open_mutex); |
422 | snd_use_lock_free(&mdev->use_lock); |
423 | return 0; |
424 | } |
425 | |
426 | /* |
427 | * change seq capability flags to file mode flags |
428 | */ |
429 | int |
430 | snd_seq_oss_midi_filemode(struct seq_oss_devinfo *dp, int dev) |
431 | { |
432 | struct seq_oss_midi *mdev; |
433 | int mode; |
434 | |
435 | mdev = get_mididev(dp, dev); |
436 | if (!mdev) |
437 | return 0; |
438 | |
439 | mode = 0; |
440 | if (mdev->opened & PERM_WRITE) |
441 | mode |= SNDRV_SEQ_OSS_FILE_WRITE; |
442 | if (mdev->opened & PERM_READ) |
443 | mode |= SNDRV_SEQ_OSS_FILE_READ; |
444 | |
445 | snd_use_lock_free(&mdev->use_lock); |
446 | return mode; |
447 | } |
448 | |
449 | /* |
450 | * reset the midi device and close it: |
451 | * so far, only close the device. |
452 | */ |
453 | void |
454 | snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev) |
455 | { |
456 | struct seq_oss_midi *mdev; |
457 | |
458 | mdev = get_mididev(dp, dev); |
459 | if (!mdev) |
460 | return; |
461 | if (! mdev->opened) { |
462 | snd_use_lock_free(&mdev->use_lock); |
463 | return; |
464 | } |
465 | |
466 | if (mdev->opened & PERM_WRITE) { |
467 | struct snd_seq_event ev; |
468 | int c; |
469 | |
470 | memset(&ev, 0, sizeof(ev)); |
471 | ev.dest.client = mdev->client; |
472 | ev.dest.port = mdev->port; |
473 | ev.queue = dp->queue; |
474 | ev.source.port = dp->port; |
475 | if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) { |
476 | ev.type = SNDRV_SEQ_EVENT_SENSING; |
477 | snd_seq_oss_dispatch(dp, ev: &ev, atomic: 0, hop: 0); |
478 | } |
479 | for (c = 0; c < 16; c++) { |
480 | ev.type = SNDRV_SEQ_EVENT_CONTROLLER; |
481 | ev.data.control.channel = c; |
482 | ev.data.control.param = MIDI_CTL_ALL_NOTES_OFF; |
483 | snd_seq_oss_dispatch(dp, ev: &ev, atomic: 0, hop: 0); |
484 | if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) { |
485 | ev.data.control.param = |
486 | MIDI_CTL_RESET_CONTROLLERS; |
487 | snd_seq_oss_dispatch(dp, ev: &ev, atomic: 0, hop: 0); |
488 | ev.type = SNDRV_SEQ_EVENT_PITCHBEND; |
489 | ev.data.control.value = 0; |
490 | snd_seq_oss_dispatch(dp, ev: &ev, atomic: 0, hop: 0); |
491 | } |
492 | } |
493 | } |
494 | // snd_seq_oss_midi_close(dp, dev); |
495 | snd_use_lock_free(&mdev->use_lock); |
496 | } |
497 | |
498 | |
499 | /* |
500 | * get client/port of the specified MIDI device |
501 | */ |
502 | void |
503 | snd_seq_oss_midi_get_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_addr *addr) |
504 | { |
505 | struct seq_oss_midi *mdev; |
506 | |
507 | mdev = get_mididev(dp, dev); |
508 | if (!mdev) |
509 | return; |
510 | addr->client = mdev->client; |
511 | addr->port = mdev->port; |
512 | snd_use_lock_free(&mdev->use_lock); |
513 | } |
514 | |
515 | |
516 | /* |
517 | * input callback - this can be atomic |
518 | */ |
519 | int |
520 | snd_seq_oss_midi_input(struct snd_seq_event *ev, int direct, void *private_data) |
521 | { |
522 | struct seq_oss_devinfo *dp = (struct seq_oss_devinfo *)private_data; |
523 | struct seq_oss_midi *mdev; |
524 | int rc; |
525 | |
526 | if (dp->readq == NULL) |
527 | return 0; |
528 | mdev = find_slot(client: ev->source.client, port: ev->source.port); |
529 | if (!mdev) |
530 | return 0; |
531 | if (! (mdev->opened & PERM_READ)) { |
532 | snd_use_lock_free(&mdev->use_lock); |
533 | return 0; |
534 | } |
535 | |
536 | if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) |
537 | rc = send_synth_event(dp, ev, dev: mdev->seq_device); |
538 | else |
539 | rc = send_midi_event(dp, ev, mdev); |
540 | |
541 | snd_use_lock_free(&mdev->use_lock); |
542 | return rc; |
543 | } |
544 | |
545 | /* |
546 | * convert ALSA sequencer event to OSS synth event |
547 | */ |
548 | static int |
549 | send_synth_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, int dev) |
550 | { |
551 | union evrec ossev; |
552 | |
553 | memset(&ossev, 0, sizeof(ossev)); |
554 | |
555 | switch (ev->type) { |
556 | case SNDRV_SEQ_EVENT_NOTEON: |
557 | ossev.v.cmd = MIDI_NOTEON; break; |
558 | case SNDRV_SEQ_EVENT_NOTEOFF: |
559 | ossev.v.cmd = MIDI_NOTEOFF; break; |
560 | case SNDRV_SEQ_EVENT_KEYPRESS: |
561 | ossev.v.cmd = MIDI_KEY_PRESSURE; break; |
562 | case SNDRV_SEQ_EVENT_CONTROLLER: |
563 | ossev.l.cmd = MIDI_CTL_CHANGE; break; |
564 | case SNDRV_SEQ_EVENT_PGMCHANGE: |
565 | ossev.l.cmd = MIDI_PGM_CHANGE; break; |
566 | case SNDRV_SEQ_EVENT_CHANPRESS: |
567 | ossev.l.cmd = MIDI_CHN_PRESSURE; break; |
568 | case SNDRV_SEQ_EVENT_PITCHBEND: |
569 | ossev.l.cmd = MIDI_PITCH_BEND; break; |
570 | default: |
571 | return 0; /* not supported */ |
572 | } |
573 | |
574 | ossev.v.dev = dev; |
575 | |
576 | switch (ev->type) { |
577 | case SNDRV_SEQ_EVENT_NOTEON: |
578 | case SNDRV_SEQ_EVENT_NOTEOFF: |
579 | case SNDRV_SEQ_EVENT_KEYPRESS: |
580 | ossev.v.code = EV_CHN_VOICE; |
581 | ossev.v.note = ev->data.note.note; |
582 | ossev.v.parm = ev->data.note.velocity; |
583 | ossev.v.chn = ev->data.note.channel; |
584 | break; |
585 | case SNDRV_SEQ_EVENT_CONTROLLER: |
586 | case SNDRV_SEQ_EVENT_PGMCHANGE: |
587 | case SNDRV_SEQ_EVENT_CHANPRESS: |
588 | ossev.l.code = EV_CHN_COMMON; |
589 | ossev.l.p1 = ev->data.control.param; |
590 | ossev.l.val = ev->data.control.value; |
591 | ossev.l.chn = ev->data.control.channel; |
592 | break; |
593 | case SNDRV_SEQ_EVENT_PITCHBEND: |
594 | ossev.l.code = EV_CHN_COMMON; |
595 | ossev.l.val = ev->data.control.value + 8192; |
596 | ossev.l.chn = ev->data.control.channel; |
597 | break; |
598 | } |
599 | |
600 | snd_seq_oss_readq_put_timestamp(readq: dp->readq, curt: ev->time.tick, seq_mode: dp->seq_mode); |
601 | snd_seq_oss_readq_put_event(readq: dp->readq, ev: &ossev); |
602 | |
603 | return 0; |
604 | } |
605 | |
606 | /* |
607 | * decode event and send MIDI bytes to read queue |
608 | */ |
609 | static int |
610 | send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, struct seq_oss_midi *mdev) |
611 | { |
612 | char msg[32]; |
613 | int len; |
614 | |
615 | snd_seq_oss_readq_put_timestamp(readq: dp->readq, curt: ev->time.tick, seq_mode: dp->seq_mode); |
616 | if (!dp->timer->running) |
617 | len = snd_seq_oss_timer_start(timer: dp->timer); |
618 | if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { |
619 | snd_seq_oss_readq_sysex(q: dp->readq, dev: mdev->seq_device, ev); |
620 | snd_midi_event_reset_decode(dev: mdev->coder); |
621 | } else { |
622 | len = snd_midi_event_decode(dev: mdev->coder, buf: msg, count: sizeof(msg), ev); |
623 | if (len > 0) |
624 | snd_seq_oss_readq_puts(readq: dp->readq, dev: mdev->seq_device, data: msg, len); |
625 | } |
626 | |
627 | return 0; |
628 | } |
629 | |
630 | |
631 | /* |
632 | * dump midi data |
633 | * return 0 : enqueued |
634 | * non-zero : invalid - ignored |
635 | */ |
636 | int |
637 | snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c, struct snd_seq_event *ev) |
638 | { |
639 | struct seq_oss_midi *mdev; |
640 | |
641 | mdev = get_mididev(dp, dev); |
642 | if (!mdev) |
643 | return -ENODEV; |
644 | if (snd_midi_event_encode_byte(dev: mdev->coder, c, ev)) { |
645 | snd_seq_oss_fill_addr(dp, ev, dest_client: mdev->client, dest_port: mdev->port); |
646 | snd_use_lock_free(&mdev->use_lock); |
647 | return 0; |
648 | } |
649 | snd_use_lock_free(&mdev->use_lock); |
650 | return -EINVAL; |
651 | } |
652 | |
653 | /* |
654 | * create OSS compatible midi_info record |
655 | */ |
656 | int |
657 | snd_seq_oss_midi_make_info(struct seq_oss_devinfo *dp, int dev, struct midi_info *inf) |
658 | { |
659 | struct seq_oss_midi *mdev; |
660 | |
661 | mdev = get_mididev(dp, dev); |
662 | if (!mdev) |
663 | return -ENXIO; |
664 | inf->device = dev; |
665 | inf->dev_type = 0; /* FIXME: ?? */ |
666 | inf->capabilities = 0; /* FIXME: ?? */ |
667 | strscpy(inf->name, mdev->name, sizeof(inf->name)); |
668 | snd_use_lock_free(&mdev->use_lock); |
669 | return 0; |
670 | } |
671 | |
672 | |
673 | #ifdef CONFIG_SND_PROC_FS |
674 | /* |
675 | * proc interface |
676 | */ |
677 | static char * |
678 | capmode_str(int val) |
679 | { |
680 | val &= PERM_READ|PERM_WRITE; |
681 | if (val == (PERM_READ|PERM_WRITE)) |
682 | return "read/write" ; |
683 | else if (val == PERM_READ) |
684 | return "read" ; |
685 | else if (val == PERM_WRITE) |
686 | return "write" ; |
687 | else |
688 | return "none" ; |
689 | } |
690 | |
691 | void |
692 | snd_seq_oss_midi_info_read(struct snd_info_buffer *buf) |
693 | { |
694 | int i; |
695 | struct seq_oss_midi *mdev; |
696 | |
697 | snd_iprintf(buf, "\nNumber of MIDI devices: %d\n" , max_midi_devs); |
698 | for (i = 0; i < max_midi_devs; i++) { |
699 | snd_iprintf(buf, "\nmidi %d: " , i); |
700 | mdev = get_mdev(dev: i); |
701 | if (mdev == NULL) { |
702 | snd_iprintf(buf, "*empty*\n" ); |
703 | continue; |
704 | } |
705 | snd_iprintf(buf, "[%s] ALSA port %d:%d\n" , mdev->name, |
706 | mdev->client, mdev->port); |
707 | snd_iprintf(buf, " capability %s / opened %s\n" , |
708 | capmode_str(mdev->flags), |
709 | capmode_str(mdev->opened)); |
710 | snd_use_lock_free(&mdev->use_lock); |
711 | } |
712 | } |
713 | #endif /* CONFIG_SND_PROC_FS */ |
714 | |