1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * ALSA sequencer Client Manager |
4 | * Copyright (c) 1998-2001 by Frank van de Pol <fvdpol@coil.demon.nl> |
5 | * Jaroslav Kysela <perex@perex.cz> |
6 | * Takashi Iwai <tiwai@suse.de> |
7 | */ |
8 | |
9 | #include <linux/init.h> |
10 | #include <linux/export.h> |
11 | #include <linux/slab.h> |
12 | #include <sound/core.h> |
13 | #include <sound/minors.h> |
14 | #include <linux/kmod.h> |
15 | |
16 | #include <sound/seq_kernel.h> |
17 | #include <sound/ump.h> |
18 | #include "seq_clientmgr.h" |
19 | #include "seq_memory.h" |
20 | #include "seq_queue.h" |
21 | #include "seq_timer.h" |
22 | #include "seq_info.h" |
23 | #include "seq_system.h" |
24 | #include "seq_ump_convert.h" |
25 | #include <sound/seq_device.h> |
26 | #ifdef CONFIG_COMPAT |
27 | #include <linux/compat.h> |
28 | #endif |
29 | |
30 | /* Client Manager |
31 | |
32 | * this module handles the connections of userland and kernel clients |
33 | * |
34 | */ |
35 | |
36 | /* |
37 | * There are four ranges of client numbers (last two shared): |
38 | * 0..15: global clients |
39 | * 16..127: statically allocated client numbers for cards 0..27 |
40 | * 128..191: dynamically allocated client numbers for cards 28..31 |
41 | * 128..191: dynamically allocated client numbers for applications |
42 | */ |
43 | |
44 | /* number of kernel non-card clients */ |
45 | #define SNDRV_SEQ_GLOBAL_CLIENTS 16 |
46 | /* clients per cards, for static clients */ |
47 | #define SNDRV_SEQ_CLIENTS_PER_CARD 4 |
48 | /* dynamically allocated client numbers (both kernel drivers and user space) */ |
49 | #define SNDRV_SEQ_DYNAMIC_CLIENTS_BEGIN 128 |
50 | |
51 | #define SNDRV_SEQ_LFLG_INPUT 0x0001 |
52 | #define SNDRV_SEQ_LFLG_OUTPUT 0x0002 |
53 | #define SNDRV_SEQ_LFLG_OPEN (SNDRV_SEQ_LFLG_INPUT|SNDRV_SEQ_LFLG_OUTPUT) |
54 | |
55 | static DEFINE_SPINLOCK(clients_lock); |
56 | static DEFINE_MUTEX(register_mutex); |
57 | |
58 | /* |
59 | * client table |
60 | */ |
61 | static char clienttablock[SNDRV_SEQ_MAX_CLIENTS]; |
62 | static struct snd_seq_client *clienttab[SNDRV_SEQ_MAX_CLIENTS]; |
63 | static struct snd_seq_usage client_usage; |
64 | |
65 | /* |
66 | * prototypes |
67 | */ |
68 | static int bounce_error_event(struct snd_seq_client *client, |
69 | struct snd_seq_event *event, |
70 | int err, int atomic, int hop); |
71 | static int snd_seq_deliver_single_event(struct snd_seq_client *client, |
72 | struct snd_seq_event *event, |
73 | int filter, int atomic, int hop); |
74 | |
75 | #if IS_ENABLED(CONFIG_SND_SEQ_UMP) |
76 | static void free_ump_info(struct snd_seq_client *client); |
77 | #endif |
78 | |
79 | /* |
80 | */ |
81 | static inline unsigned short snd_seq_file_flags(struct file *file) |
82 | { |
83 | switch (file->f_mode & (FMODE_READ | FMODE_WRITE)) { |
84 | case FMODE_WRITE: |
85 | return SNDRV_SEQ_LFLG_OUTPUT; |
86 | case FMODE_READ: |
87 | return SNDRV_SEQ_LFLG_INPUT; |
88 | default: |
89 | return SNDRV_SEQ_LFLG_OPEN; |
90 | } |
91 | } |
92 | |
93 | static inline int snd_seq_write_pool_allocated(struct snd_seq_client *client) |
94 | { |
95 | return snd_seq_total_cells(pool: client->pool) > 0; |
96 | } |
97 | |
98 | /* return pointer to client structure for specified id */ |
99 | static struct snd_seq_client *clientptr(int clientid) |
100 | { |
101 | if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) { |
102 | pr_debug("ALSA: seq: oops. Trying to get pointer to client %d\n" , |
103 | clientid); |
104 | return NULL; |
105 | } |
106 | return clienttab[clientid]; |
107 | } |
108 | |
109 | struct snd_seq_client *snd_seq_client_use_ptr(int clientid) |
110 | { |
111 | unsigned long flags; |
112 | struct snd_seq_client *client; |
113 | |
114 | if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) { |
115 | pr_debug("ALSA: seq: oops. Trying to get pointer to client %d\n" , |
116 | clientid); |
117 | return NULL; |
118 | } |
119 | spin_lock_irqsave(&clients_lock, flags); |
120 | client = clientptr(clientid); |
121 | if (client) |
122 | goto __lock; |
123 | if (clienttablock[clientid]) { |
124 | spin_unlock_irqrestore(lock: &clients_lock, flags); |
125 | return NULL; |
126 | } |
127 | spin_unlock_irqrestore(lock: &clients_lock, flags); |
128 | #ifdef CONFIG_MODULES |
129 | if (!in_interrupt()) { |
130 | static DECLARE_BITMAP(client_requested, SNDRV_SEQ_GLOBAL_CLIENTS); |
131 | static DECLARE_BITMAP(card_requested, SNDRV_CARDS); |
132 | |
133 | if (clientid < SNDRV_SEQ_GLOBAL_CLIENTS) { |
134 | int idx; |
135 | |
136 | if (!test_and_set_bit(nr: clientid, addr: client_requested)) { |
137 | for (idx = 0; idx < 15; idx++) { |
138 | if (seq_client_load[idx] < 0) |
139 | break; |
140 | if (seq_client_load[idx] == clientid) { |
141 | request_module("snd-seq-client-%i" , |
142 | clientid); |
143 | break; |
144 | } |
145 | } |
146 | } |
147 | } else if (clientid < SNDRV_SEQ_DYNAMIC_CLIENTS_BEGIN) { |
148 | int card = (clientid - SNDRV_SEQ_GLOBAL_CLIENTS) / |
149 | SNDRV_SEQ_CLIENTS_PER_CARD; |
150 | if (card < snd_ecards_limit) { |
151 | if (!test_and_set_bit(nr: card, addr: card_requested)) |
152 | snd_request_card(card); |
153 | snd_seq_device_load_drivers(); |
154 | } |
155 | } |
156 | spin_lock_irqsave(&clients_lock, flags); |
157 | client = clientptr(clientid); |
158 | if (client) |
159 | goto __lock; |
160 | spin_unlock_irqrestore(lock: &clients_lock, flags); |
161 | } |
162 | #endif |
163 | return NULL; |
164 | |
165 | __lock: |
166 | snd_use_lock_use(&client->use_lock); |
167 | spin_unlock_irqrestore(lock: &clients_lock, flags); |
168 | return client; |
169 | } |
170 | |
171 | /* Take refcount and perform ioctl_mutex lock on the given client; |
172 | * used only for OSS sequencer |
173 | * Unlock via snd_seq_client_ioctl_unlock() below |
174 | */ |
175 | bool snd_seq_client_ioctl_lock(int clientid) |
176 | { |
177 | struct snd_seq_client *client; |
178 | |
179 | client = snd_seq_client_use_ptr(clientid); |
180 | if (!client) |
181 | return false; |
182 | mutex_lock(&client->ioctl_mutex); |
183 | /* The client isn't unrefed here; see snd_seq_client_ioctl_unlock() */ |
184 | return true; |
185 | } |
186 | EXPORT_SYMBOL_GPL(snd_seq_client_ioctl_lock); |
187 | |
188 | /* Unlock and unref the given client; for OSS sequencer use only */ |
189 | void snd_seq_client_ioctl_unlock(int clientid) |
190 | { |
191 | struct snd_seq_client *client; |
192 | |
193 | client = snd_seq_client_use_ptr(clientid); |
194 | if (WARN_ON(!client)) |
195 | return; |
196 | mutex_unlock(lock: &client->ioctl_mutex); |
197 | /* The doubly unrefs below are intentional; the first one releases the |
198 | * leftover from snd_seq_client_ioctl_lock() above, and the second one |
199 | * is for releasing snd_seq_client_use_ptr() in this function |
200 | */ |
201 | snd_seq_client_unlock(client); |
202 | snd_seq_client_unlock(client); |
203 | } |
204 | EXPORT_SYMBOL_GPL(snd_seq_client_ioctl_unlock); |
205 | |
206 | static void usage_alloc(struct snd_seq_usage *res, int num) |
207 | { |
208 | res->cur += num; |
209 | if (res->cur > res->peak) |
210 | res->peak = res->cur; |
211 | } |
212 | |
213 | static void usage_free(struct snd_seq_usage *res, int num) |
214 | { |
215 | res->cur -= num; |
216 | } |
217 | |
218 | /* initialise data structures */ |
219 | int __init client_init_data(void) |
220 | { |
221 | /* zap out the client table */ |
222 | memset(&clienttablock, 0, sizeof(clienttablock)); |
223 | memset(&clienttab, 0, sizeof(clienttab)); |
224 | return 0; |
225 | } |
226 | |
227 | |
228 | static struct snd_seq_client *seq_create_client1(int client_index, int poolsize) |
229 | { |
230 | int c; |
231 | struct snd_seq_client *client; |
232 | |
233 | /* init client data */ |
234 | client = kzalloc(size: sizeof(*client), GFP_KERNEL); |
235 | if (client == NULL) |
236 | return NULL; |
237 | client->pool = snd_seq_pool_new(poolsize); |
238 | if (client->pool == NULL) { |
239 | kfree(objp: client); |
240 | return NULL; |
241 | } |
242 | client->type = NO_CLIENT; |
243 | snd_use_lock_init(&client->use_lock); |
244 | rwlock_init(&client->ports_lock); |
245 | mutex_init(&client->ports_mutex); |
246 | INIT_LIST_HEAD(list: &client->ports_list_head); |
247 | mutex_init(&client->ioctl_mutex); |
248 | client->ump_endpoint_port = -1; |
249 | |
250 | /* find free slot in the client table */ |
251 | spin_lock_irq(lock: &clients_lock); |
252 | if (client_index < 0) { |
253 | for (c = SNDRV_SEQ_DYNAMIC_CLIENTS_BEGIN; |
254 | c < SNDRV_SEQ_MAX_CLIENTS; |
255 | c++) { |
256 | if (clienttab[c] || clienttablock[c]) |
257 | continue; |
258 | clienttab[client->number = c] = client; |
259 | spin_unlock_irq(lock: &clients_lock); |
260 | return client; |
261 | } |
262 | } else { |
263 | if (clienttab[client_index] == NULL && !clienttablock[client_index]) { |
264 | clienttab[client->number = client_index] = client; |
265 | spin_unlock_irq(lock: &clients_lock); |
266 | return client; |
267 | } |
268 | } |
269 | spin_unlock_irq(lock: &clients_lock); |
270 | snd_seq_pool_delete(pool: &client->pool); |
271 | kfree(objp: client); |
272 | return NULL; /* no free slot found or busy, return failure code */ |
273 | } |
274 | |
275 | |
276 | static int seq_free_client1(struct snd_seq_client *client) |
277 | { |
278 | if (!client) |
279 | return 0; |
280 | spin_lock_irq(lock: &clients_lock); |
281 | clienttablock[client->number] = 1; |
282 | clienttab[client->number] = NULL; |
283 | spin_unlock_irq(lock: &clients_lock); |
284 | snd_seq_delete_all_ports(client); |
285 | snd_seq_queue_client_leave(client: client->number); |
286 | snd_use_lock_sync(&client->use_lock); |
287 | if (client->pool) |
288 | snd_seq_pool_delete(pool: &client->pool); |
289 | spin_lock_irq(lock: &clients_lock); |
290 | clienttablock[client->number] = 0; |
291 | spin_unlock_irq(lock: &clients_lock); |
292 | return 0; |
293 | } |
294 | |
295 | |
296 | static void seq_free_client(struct snd_seq_client * client) |
297 | { |
298 | mutex_lock(®ister_mutex); |
299 | switch (client->type) { |
300 | case NO_CLIENT: |
301 | pr_warn("ALSA: seq: Trying to free unused client %d\n" , |
302 | client->number); |
303 | break; |
304 | case USER_CLIENT: |
305 | case KERNEL_CLIENT: |
306 | seq_free_client1(client); |
307 | usage_free(res: &client_usage, num: 1); |
308 | break; |
309 | |
310 | default: |
311 | pr_err("ALSA: seq: Trying to free client %d with undefined type = %d\n" , |
312 | client->number, client->type); |
313 | } |
314 | mutex_unlock(lock: ®ister_mutex); |
315 | |
316 | snd_seq_system_client_ev_client_exit(client->number); |
317 | } |
318 | |
319 | |
320 | |
321 | /* -------------------------------------------------------- */ |
322 | |
323 | /* create a user client */ |
324 | static int snd_seq_open(struct inode *inode, struct file *file) |
325 | { |
326 | int c, mode; /* client id */ |
327 | struct snd_seq_client *client; |
328 | struct snd_seq_user_client *user; |
329 | int err; |
330 | |
331 | err = stream_open(inode, filp: file); |
332 | if (err < 0) |
333 | return err; |
334 | |
335 | mutex_lock(®ister_mutex); |
336 | client = seq_create_client1(client_index: -1, SNDRV_SEQ_DEFAULT_EVENTS); |
337 | if (!client) { |
338 | mutex_unlock(lock: ®ister_mutex); |
339 | return -ENOMEM; /* failure code */ |
340 | } |
341 | |
342 | mode = snd_seq_file_flags(file); |
343 | if (mode & SNDRV_SEQ_LFLG_INPUT) |
344 | client->accept_input = 1; |
345 | if (mode & SNDRV_SEQ_LFLG_OUTPUT) |
346 | client->accept_output = 1; |
347 | |
348 | user = &client->data.user; |
349 | user->fifo = NULL; |
350 | user->fifo_pool_size = 0; |
351 | |
352 | if (mode & SNDRV_SEQ_LFLG_INPUT) { |
353 | user->fifo_pool_size = SNDRV_SEQ_DEFAULT_CLIENT_EVENTS; |
354 | user->fifo = snd_seq_fifo_new(poolsize: user->fifo_pool_size); |
355 | if (user->fifo == NULL) { |
356 | seq_free_client1(client); |
357 | kfree(objp: client); |
358 | mutex_unlock(lock: ®ister_mutex); |
359 | return -ENOMEM; |
360 | } |
361 | } |
362 | |
363 | usage_alloc(res: &client_usage, num: 1); |
364 | client->type = USER_CLIENT; |
365 | mutex_unlock(lock: ®ister_mutex); |
366 | |
367 | c = client->number; |
368 | file->private_data = client; |
369 | |
370 | /* fill client data */ |
371 | user->file = file; |
372 | sprintf(buf: client->name, fmt: "Client-%d" , c); |
373 | client->data.user.owner = get_pid(pid: task_pid(current)); |
374 | |
375 | /* make others aware this new client */ |
376 | snd_seq_system_client_ev_client_start(c); |
377 | |
378 | return 0; |
379 | } |
380 | |
381 | /* delete a user client */ |
382 | static int snd_seq_release(struct inode *inode, struct file *file) |
383 | { |
384 | struct snd_seq_client *client = file->private_data; |
385 | |
386 | if (client) { |
387 | seq_free_client(client); |
388 | if (client->data.user.fifo) |
389 | snd_seq_fifo_delete(f: &client->data.user.fifo); |
390 | #if IS_ENABLED(CONFIG_SND_SEQ_UMP) |
391 | free_ump_info(client); |
392 | #endif |
393 | put_pid(pid: client->data.user.owner); |
394 | kfree(objp: client); |
395 | } |
396 | |
397 | return 0; |
398 | } |
399 | |
400 | static bool event_is_compatible(const struct snd_seq_client *client, |
401 | const struct snd_seq_event *ev) |
402 | { |
403 | if (snd_seq_ev_is_ump(ev) && !client->midi_version) |
404 | return false; |
405 | if (snd_seq_ev_is_ump(ev) && snd_seq_ev_is_variable(ev)) |
406 | return false; |
407 | return true; |
408 | } |
409 | |
410 | /* handle client read() */ |
411 | /* possible error values: |
412 | * -ENXIO invalid client or file open mode |
413 | * -ENOSPC FIFO overflow (the flag is cleared after this error report) |
414 | * -EINVAL no enough user-space buffer to write the whole event |
415 | * -EFAULT seg. fault during copy to user space |
416 | */ |
417 | static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count, |
418 | loff_t *offset) |
419 | { |
420 | struct snd_seq_client *client = file->private_data; |
421 | struct snd_seq_fifo *fifo; |
422 | size_t aligned_size; |
423 | int err; |
424 | long result = 0; |
425 | struct snd_seq_event_cell *cell; |
426 | |
427 | if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT)) |
428 | return -ENXIO; |
429 | |
430 | if (!access_ok(buf, count)) |
431 | return -EFAULT; |
432 | |
433 | /* check client structures are in place */ |
434 | if (snd_BUG_ON(!client)) |
435 | return -ENXIO; |
436 | |
437 | if (!client->accept_input) |
438 | return -ENXIO; |
439 | fifo = client->data.user.fifo; |
440 | if (!fifo) |
441 | return -ENXIO; |
442 | |
443 | if (atomic_read(v: &fifo->overflow) > 0) { |
444 | /* buffer overflow is detected */ |
445 | snd_seq_fifo_clear(f: fifo); |
446 | /* return error code */ |
447 | return -ENOSPC; |
448 | } |
449 | |
450 | cell = NULL; |
451 | err = 0; |
452 | snd_seq_fifo_lock(fifo); |
453 | |
454 | if (IS_ENABLED(CONFIG_SND_SEQ_UMP) && client->midi_version > 0) |
455 | aligned_size = sizeof(struct snd_seq_ump_event); |
456 | else |
457 | aligned_size = sizeof(struct snd_seq_event); |
458 | |
459 | /* while data available in queue */ |
460 | while (count >= aligned_size) { |
461 | int nonblock; |
462 | |
463 | nonblock = (file->f_flags & O_NONBLOCK) || result > 0; |
464 | err = snd_seq_fifo_cell_out(f: fifo, cellp: &cell, nonblock); |
465 | if (err < 0) |
466 | break; |
467 | if (!event_is_compatible(client, ev: &cell->event)) { |
468 | snd_seq_cell_free(cell); |
469 | cell = NULL; |
470 | continue; |
471 | } |
472 | if (snd_seq_ev_is_variable(&cell->event)) { |
473 | struct snd_seq_ump_event tmpev; |
474 | |
475 | memcpy(&tmpev, &cell->event, aligned_size); |
476 | tmpev.data.ext.len &= ~SNDRV_SEQ_EXT_MASK; |
477 | if (copy_to_user(to: buf, from: &tmpev, n: aligned_size)) { |
478 | err = -EFAULT; |
479 | break; |
480 | } |
481 | count -= aligned_size; |
482 | buf += aligned_size; |
483 | err = snd_seq_expand_var_event(event: &cell->event, count, |
484 | buf: (char __force *)buf, in_kernel: 0, |
485 | size_aligned: aligned_size); |
486 | if (err < 0) |
487 | break; |
488 | result += err; |
489 | count -= err; |
490 | buf += err; |
491 | } else { |
492 | if (copy_to_user(to: buf, from: &cell->event, n: aligned_size)) { |
493 | err = -EFAULT; |
494 | break; |
495 | } |
496 | count -= aligned_size; |
497 | buf += aligned_size; |
498 | } |
499 | snd_seq_cell_free(cell); |
500 | cell = NULL; /* to be sure */ |
501 | result += aligned_size; |
502 | } |
503 | |
504 | if (err < 0) { |
505 | if (cell) |
506 | snd_seq_fifo_cell_putback(f: fifo, cell); |
507 | if (err == -EAGAIN && result > 0) |
508 | err = 0; |
509 | } |
510 | snd_seq_fifo_unlock(fifo); |
511 | |
512 | return (err < 0) ? err : result; |
513 | } |
514 | |
515 | |
516 | /* |
517 | * check access permission to the port |
518 | */ |
519 | static int check_port_perm(struct snd_seq_client_port *port, unsigned int flags) |
520 | { |
521 | if ((port->capability & flags) != flags) |
522 | return 0; |
523 | return flags; |
524 | } |
525 | |
526 | /* |
527 | * check if the destination client is available, and return the pointer |
528 | * if filter is non-zero, client filter bitmap is tested. |
529 | */ |
530 | static struct snd_seq_client *get_event_dest_client(struct snd_seq_event *event, |
531 | int filter) |
532 | { |
533 | struct snd_seq_client *dest; |
534 | |
535 | dest = snd_seq_client_use_ptr(clientid: event->dest.client); |
536 | if (dest == NULL) |
537 | return NULL; |
538 | if (! dest->accept_input) |
539 | goto __not_avail; |
540 | if ((dest->filter & SNDRV_SEQ_FILTER_USE_EVENT) && |
541 | ! test_bit(event->type, dest->event_filter)) |
542 | goto __not_avail; |
543 | if (filter && !(dest->filter & filter)) |
544 | goto __not_avail; |
545 | |
546 | return dest; /* ok - accessible */ |
547 | __not_avail: |
548 | snd_seq_client_unlock(dest); |
549 | return NULL; |
550 | } |
551 | |
552 | |
553 | /* |
554 | * Return the error event. |
555 | * |
556 | * If the receiver client is a user client, the original event is |
557 | * encapsulated in SNDRV_SEQ_EVENT_BOUNCE as variable length event. If |
558 | * the original event is also variable length, the external data is |
559 | * copied after the event record. |
560 | * If the receiver client is a kernel client, the original event is |
561 | * quoted in SNDRV_SEQ_EVENT_KERNEL_ERROR, since this requires no extra |
562 | * kmalloc. |
563 | */ |
564 | static int bounce_error_event(struct snd_seq_client *client, |
565 | struct snd_seq_event *event, |
566 | int err, int atomic, int hop) |
567 | { |
568 | struct snd_seq_event bounce_ev; |
569 | int result; |
570 | |
571 | if (client == NULL || |
572 | ! (client->filter & SNDRV_SEQ_FILTER_BOUNCE) || |
573 | ! client->accept_input) |
574 | return 0; /* ignored */ |
575 | |
576 | /* set up quoted error */ |
577 | memset(&bounce_ev, 0, sizeof(bounce_ev)); |
578 | bounce_ev.type = SNDRV_SEQ_EVENT_KERNEL_ERROR; |
579 | bounce_ev.flags = SNDRV_SEQ_EVENT_LENGTH_FIXED; |
580 | bounce_ev.queue = SNDRV_SEQ_QUEUE_DIRECT; |
581 | bounce_ev.source.client = SNDRV_SEQ_CLIENT_SYSTEM; |
582 | bounce_ev.source.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; |
583 | bounce_ev.dest.client = client->number; |
584 | bounce_ev.dest.port = event->source.port; |
585 | bounce_ev.data.quote.origin = event->dest; |
586 | bounce_ev.data.quote.event = event; |
587 | bounce_ev.data.quote.value = -err; /* use positive value */ |
588 | result = snd_seq_deliver_single_event(NULL, event: &bounce_ev, filter: 0, atomic, hop: hop + 1); |
589 | if (result < 0) { |
590 | client->event_lost++; |
591 | return result; |
592 | } |
593 | |
594 | return result; |
595 | } |
596 | |
597 | |
598 | /* |
599 | * rewrite the time-stamp of the event record with the curren time |
600 | * of the given queue. |
601 | * return non-zero if updated. |
602 | */ |
603 | static int update_timestamp_of_queue(struct snd_seq_event *event, |
604 | int queue, int real_time) |
605 | { |
606 | struct snd_seq_queue *q; |
607 | |
608 | q = queueptr(queueid: queue); |
609 | if (! q) |
610 | return 0; |
611 | event->queue = queue; |
612 | event->flags &= ~SNDRV_SEQ_TIME_STAMP_MASK; |
613 | if (real_time) { |
614 | event->time.time = snd_seq_timer_get_cur_time(tmr: q->timer, adjust_ktime: true); |
615 | event->flags |= SNDRV_SEQ_TIME_STAMP_REAL; |
616 | } else { |
617 | event->time.tick = snd_seq_timer_get_cur_tick(tmr: q->timer); |
618 | event->flags |= SNDRV_SEQ_TIME_STAMP_TICK; |
619 | } |
620 | queuefree(q); |
621 | return 1; |
622 | } |
623 | |
624 | /* deliver a single event; called from below and UMP converter */ |
625 | int __snd_seq_deliver_single_event(struct snd_seq_client *dest, |
626 | struct snd_seq_client_port *dest_port, |
627 | struct snd_seq_event *event, |
628 | int atomic, int hop) |
629 | { |
630 | switch (dest->type) { |
631 | case USER_CLIENT: |
632 | if (!dest->data.user.fifo) |
633 | return 0; |
634 | return snd_seq_fifo_event_in(f: dest->data.user.fifo, event); |
635 | case KERNEL_CLIENT: |
636 | if (!dest_port->event_input) |
637 | return 0; |
638 | return dest_port->event_input(event, |
639 | snd_seq_ev_is_direct(event), |
640 | dest_port->private_data, |
641 | atomic, hop); |
642 | } |
643 | return 0; |
644 | } |
645 | |
646 | /* |
647 | * deliver an event to the specified destination. |
648 | * if filter is non-zero, client filter bitmap is tested. |
649 | * |
650 | * RETURN VALUE: 0 : if succeeded |
651 | * <0 : error |
652 | */ |
653 | static int snd_seq_deliver_single_event(struct snd_seq_client *client, |
654 | struct snd_seq_event *event, |
655 | int filter, int atomic, int hop) |
656 | { |
657 | struct snd_seq_client *dest = NULL; |
658 | struct snd_seq_client_port *dest_port = NULL; |
659 | int result = -ENOENT; |
660 | int direct; |
661 | |
662 | direct = snd_seq_ev_is_direct(event); |
663 | |
664 | dest = get_event_dest_client(event, filter); |
665 | if (dest == NULL) |
666 | goto __skip; |
667 | dest_port = snd_seq_port_use_ptr(client: dest, num: event->dest.port); |
668 | if (dest_port == NULL) |
669 | goto __skip; |
670 | |
671 | /* check permission */ |
672 | if (! check_port_perm(port: dest_port, SNDRV_SEQ_PORT_CAP_WRITE)) { |
673 | result = -EPERM; |
674 | goto __skip; |
675 | } |
676 | |
677 | if (dest_port->timestamping) |
678 | update_timestamp_of_queue(event, queue: dest_port->time_queue, |
679 | real_time: dest_port->time_real); |
680 | |
681 | #if IS_ENABLED(CONFIG_SND_SEQ_UMP) |
682 | if (!(dest->filter & SNDRV_SEQ_FILTER_NO_CONVERT)) { |
683 | if (snd_seq_ev_is_ump(event)) { |
684 | result = snd_seq_deliver_from_ump(source: client, dest, dest_port, |
685 | event, atomic, hop); |
686 | goto __skip; |
687 | } else if (snd_seq_client_is_ump(c: dest)) { |
688 | result = snd_seq_deliver_to_ump(source: client, dest, dest_port, |
689 | event, atomic, hop); |
690 | goto __skip; |
691 | } |
692 | } |
693 | #endif /* CONFIG_SND_SEQ_UMP */ |
694 | |
695 | result = __snd_seq_deliver_single_event(dest, dest_port, event, |
696 | atomic, hop); |
697 | |
698 | __skip: |
699 | if (dest_port) |
700 | snd_seq_port_unlock(dest_port); |
701 | if (dest) |
702 | snd_seq_client_unlock(dest); |
703 | |
704 | if (result < 0 && !direct) { |
705 | result = bounce_error_event(client, event, err: result, atomic, hop); |
706 | } |
707 | return result; |
708 | } |
709 | |
710 | |
711 | /* |
712 | * send the event to all subscribers: |
713 | */ |
714 | static int __deliver_to_subscribers(struct snd_seq_client *client, |
715 | struct snd_seq_event *event, |
716 | struct snd_seq_client_port *src_port, |
717 | int atomic, int hop) |
718 | { |
719 | struct snd_seq_subscribers *subs; |
720 | int err, result = 0, num_ev = 0; |
721 | union __snd_seq_event event_saved; |
722 | size_t saved_size; |
723 | struct snd_seq_port_subs_info *grp; |
724 | |
725 | /* save original event record */ |
726 | saved_size = snd_seq_event_packet_size(ev: event); |
727 | memcpy(&event_saved, event, saved_size); |
728 | grp = &src_port->c_src; |
729 | |
730 | /* lock list */ |
731 | if (atomic) |
732 | read_lock(&grp->list_lock); |
733 | else |
734 | down_read_nested(sem: &grp->list_mutex, subclass: hop); |
735 | list_for_each_entry(subs, &grp->list_head, src_list) { |
736 | /* both ports ready? */ |
737 | if (atomic_read(v: &subs->ref_count) != 2) |
738 | continue; |
739 | event->dest = subs->info.dest; |
740 | if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP) |
741 | /* convert time according to flag with subscription */ |
742 | update_timestamp_of_queue(event, queue: subs->info.queue, |
743 | real_time: subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL); |
744 | err = snd_seq_deliver_single_event(client, event, |
745 | filter: 0, atomic, hop); |
746 | if (err < 0) { |
747 | /* save first error that occurs and continue */ |
748 | if (!result) |
749 | result = err; |
750 | continue; |
751 | } |
752 | num_ev++; |
753 | /* restore original event record */ |
754 | memcpy(event, &event_saved, saved_size); |
755 | } |
756 | if (atomic) |
757 | read_unlock(&grp->list_lock); |
758 | else |
759 | up_read(sem: &grp->list_mutex); |
760 | memcpy(event, &event_saved, saved_size); |
761 | return (result < 0) ? result : num_ev; |
762 | } |
763 | |
764 | static int deliver_to_subscribers(struct snd_seq_client *client, |
765 | struct snd_seq_event *event, |
766 | int atomic, int hop) |
767 | { |
768 | struct snd_seq_client_port *src_port; |
769 | int ret = 0, ret2; |
770 | |
771 | src_port = snd_seq_port_use_ptr(client, num: event->source.port); |
772 | if (src_port) { |
773 | ret = __deliver_to_subscribers(client, event, src_port, atomic, hop); |
774 | snd_seq_port_unlock(src_port); |
775 | } |
776 | |
777 | if (client->ump_endpoint_port < 0 || |
778 | event->source.port == client->ump_endpoint_port) |
779 | return ret; |
780 | |
781 | src_port = snd_seq_port_use_ptr(client, num: client->ump_endpoint_port); |
782 | if (!src_port) |
783 | return ret; |
784 | ret2 = __deliver_to_subscribers(client, event, src_port, atomic, hop); |
785 | snd_seq_port_unlock(src_port); |
786 | return ret2 < 0 ? ret2 : ret; |
787 | } |
788 | |
789 | /* deliver an event to the destination port(s). |
790 | * if the event is to subscribers or broadcast, the event is dispatched |
791 | * to multiple targets. |
792 | * |
793 | * RETURN VALUE: n > 0 : the number of delivered events. |
794 | * n == 0 : the event was not passed to any client. |
795 | * n < 0 : error - event was not processed. |
796 | */ |
797 | static int snd_seq_deliver_event(struct snd_seq_client *client, struct snd_seq_event *event, |
798 | int atomic, int hop) |
799 | { |
800 | int result; |
801 | |
802 | hop++; |
803 | if (hop >= SNDRV_SEQ_MAX_HOPS) { |
804 | pr_debug("ALSA: seq: too long delivery path (%d:%d->%d:%d)\n" , |
805 | event->source.client, event->source.port, |
806 | event->dest.client, event->dest.port); |
807 | return -EMLINK; |
808 | } |
809 | |
810 | if (snd_seq_ev_is_variable(event) && |
811 | snd_BUG_ON(atomic && (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR))) |
812 | return -EINVAL; |
813 | |
814 | if (event->queue == SNDRV_SEQ_ADDRESS_SUBSCRIBERS || |
815 | event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) |
816 | result = deliver_to_subscribers(client, event, atomic, hop); |
817 | else |
818 | result = snd_seq_deliver_single_event(client, event, filter: 0, atomic, hop); |
819 | |
820 | return result; |
821 | } |
822 | |
823 | /* |
824 | * dispatch an event cell: |
825 | * This function is called only from queue check routines in timer |
826 | * interrupts or after enqueued. |
827 | * The event cell shall be released or re-queued in this function. |
828 | * |
829 | * RETURN VALUE: n > 0 : the number of delivered events. |
830 | * n == 0 : the event was not passed to any client. |
831 | * n < 0 : error - event was not processed. |
832 | */ |
833 | int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop) |
834 | { |
835 | struct snd_seq_client *client; |
836 | int result; |
837 | |
838 | if (snd_BUG_ON(!cell)) |
839 | return -EINVAL; |
840 | |
841 | client = snd_seq_client_use_ptr(clientid: cell->event.source.client); |
842 | if (client == NULL) { |
843 | snd_seq_cell_free(cell); /* release this cell */ |
844 | return -EINVAL; |
845 | } |
846 | |
847 | if (!snd_seq_ev_is_ump(&cell->event) && |
848 | cell->event.type == SNDRV_SEQ_EVENT_NOTE) { |
849 | /* NOTE event: |
850 | * the event cell is re-used as a NOTE-OFF event and |
851 | * enqueued again. |
852 | */ |
853 | struct snd_seq_event tmpev, *ev; |
854 | |
855 | /* reserve this event to enqueue note-off later */ |
856 | tmpev = cell->event; |
857 | tmpev.type = SNDRV_SEQ_EVENT_NOTEON; |
858 | result = snd_seq_deliver_event(client, event: &tmpev, atomic, hop); |
859 | |
860 | /* |
861 | * This was originally a note event. We now re-use the |
862 | * cell for the note-off event. |
863 | */ |
864 | |
865 | ev = &cell->event; |
866 | ev->type = SNDRV_SEQ_EVENT_NOTEOFF; |
867 | ev->flags |= SNDRV_SEQ_PRIORITY_HIGH; |
868 | |
869 | /* add the duration time */ |
870 | switch (ev->flags & SNDRV_SEQ_TIME_STAMP_MASK) { |
871 | case SNDRV_SEQ_TIME_STAMP_TICK: |
872 | cell->event.time.tick += ev->data.note.duration; |
873 | break; |
874 | case SNDRV_SEQ_TIME_STAMP_REAL: |
875 | /* unit for duration is ms */ |
876 | ev->time.time.tv_nsec += 1000000 * (ev->data.note.duration % 1000); |
877 | ev->time.time.tv_sec += ev->data.note.duration / 1000 + |
878 | ev->time.time.tv_nsec / 1000000000; |
879 | ev->time.time.tv_nsec %= 1000000000; |
880 | break; |
881 | } |
882 | ev->data.note.velocity = ev->data.note.off_velocity; |
883 | |
884 | /* Now queue this cell as the note off event */ |
885 | if (snd_seq_enqueue_event(cell, atomic, hop) < 0) |
886 | snd_seq_cell_free(cell); /* release this cell */ |
887 | |
888 | } else { |
889 | /* Normal events: |
890 | * event cell is freed after processing the event |
891 | */ |
892 | |
893 | result = snd_seq_deliver_event(client, event: &cell->event, atomic, hop); |
894 | snd_seq_cell_free(cell); |
895 | } |
896 | |
897 | snd_seq_client_unlock(client); |
898 | return result; |
899 | } |
900 | |
901 | |
902 | /* Allocate a cell from client pool and enqueue it to queue: |
903 | * if pool is empty and blocking is TRUE, sleep until a new cell is |
904 | * available. |
905 | */ |
906 | static int snd_seq_client_enqueue_event(struct snd_seq_client *client, |
907 | struct snd_seq_event *event, |
908 | struct file *file, int blocking, |
909 | int atomic, int hop, |
910 | struct mutex *mutexp) |
911 | { |
912 | struct snd_seq_event_cell *cell; |
913 | int err; |
914 | |
915 | /* special queue values - force direct passing */ |
916 | if (event->queue == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) { |
917 | event->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS; |
918 | event->queue = SNDRV_SEQ_QUEUE_DIRECT; |
919 | } else if (event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) { |
920 | /* check presence of source port */ |
921 | struct snd_seq_client_port *src_port = snd_seq_port_use_ptr(client, num: event->source.port); |
922 | if (src_port == NULL) |
923 | return -EINVAL; |
924 | snd_seq_port_unlock(src_port); |
925 | } |
926 | |
927 | /* direct event processing without enqueued */ |
928 | if (snd_seq_ev_is_direct(event)) { |
929 | if (!snd_seq_ev_is_ump(event) && |
930 | event->type == SNDRV_SEQ_EVENT_NOTE) |
931 | return -EINVAL; /* this event must be enqueued! */ |
932 | return snd_seq_deliver_event(client, event, atomic, hop); |
933 | } |
934 | |
935 | /* Not direct, normal queuing */ |
936 | if (snd_seq_queue_is_used(queueid: event->queue, client: client->number) <= 0) |
937 | return -EINVAL; /* invalid queue */ |
938 | if (! snd_seq_write_pool_allocated(client)) |
939 | return -ENXIO; /* queue is not allocated */ |
940 | |
941 | /* allocate an event cell */ |
942 | err = snd_seq_event_dup(pool: client->pool, event, cellp: &cell, nonblock: !blocking || atomic, |
943 | file, mutexp); |
944 | if (err < 0) |
945 | return err; |
946 | |
947 | /* we got a cell. enqueue it. */ |
948 | err = snd_seq_enqueue_event(cell, atomic, hop); |
949 | if (err < 0) { |
950 | snd_seq_cell_free(cell); |
951 | return err; |
952 | } |
953 | |
954 | return 0; |
955 | } |
956 | |
957 | |
958 | /* |
959 | * check validity of event type and data length. |
960 | * return non-zero if invalid. |
961 | */ |
962 | static int check_event_type_and_length(struct snd_seq_event *ev) |
963 | { |
964 | switch (snd_seq_ev_length_type(ev)) { |
965 | case SNDRV_SEQ_EVENT_LENGTH_FIXED: |
966 | if (snd_seq_ev_is_variable_type(ev)) |
967 | return -EINVAL; |
968 | break; |
969 | case SNDRV_SEQ_EVENT_LENGTH_VARIABLE: |
970 | if (! snd_seq_ev_is_variable_type(ev) || |
971 | (ev->data.ext.len & ~SNDRV_SEQ_EXT_MASK) >= SNDRV_SEQ_MAX_EVENT_LEN) |
972 | return -EINVAL; |
973 | break; |
974 | case SNDRV_SEQ_EVENT_LENGTH_VARUSR: |
975 | if (! snd_seq_ev_is_direct(ev)) |
976 | return -EINVAL; |
977 | break; |
978 | } |
979 | return 0; |
980 | } |
981 | |
982 | |
983 | /* handle write() */ |
984 | /* possible error values: |
985 | * -ENXIO invalid client or file open mode |
986 | * -ENOMEM malloc failed |
987 | * -EFAULT seg. fault during copy from user space |
988 | * -EINVAL invalid event |
989 | * -EAGAIN no space in output pool |
990 | * -EINTR interrupts while sleep |
991 | * -EMLINK too many hops |
992 | * others depends on return value from driver callback |
993 | */ |
994 | static ssize_t snd_seq_write(struct file *file, const char __user *buf, |
995 | size_t count, loff_t *offset) |
996 | { |
997 | struct snd_seq_client *client = file->private_data; |
998 | int written = 0, len; |
999 | int err, handled; |
1000 | union __snd_seq_event __event; |
1001 | struct snd_seq_event *ev = &__event.legacy; |
1002 | |
1003 | if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT)) |
1004 | return -ENXIO; |
1005 | |
1006 | /* check client structures are in place */ |
1007 | if (snd_BUG_ON(!client)) |
1008 | return -ENXIO; |
1009 | |
1010 | if (!client->accept_output || client->pool == NULL) |
1011 | return -ENXIO; |
1012 | |
1013 | repeat: |
1014 | handled = 0; |
1015 | /* allocate the pool now if the pool is not allocated yet */ |
1016 | mutex_lock(&client->ioctl_mutex); |
1017 | if (client->pool->size > 0 && !snd_seq_write_pool_allocated(client)) { |
1018 | err = snd_seq_pool_init(pool: client->pool); |
1019 | if (err < 0) |
1020 | goto out; |
1021 | } |
1022 | |
1023 | /* only process whole events */ |
1024 | err = -EINVAL; |
1025 | while (count >= sizeof(struct snd_seq_event)) { |
1026 | /* Read in the event header from the user */ |
1027 | len = sizeof(struct snd_seq_event); |
1028 | if (copy_from_user(to: ev, from: buf, n: len)) { |
1029 | err = -EFAULT; |
1030 | break; |
1031 | } |
1032 | /* read in the rest bytes for UMP events */ |
1033 | if (snd_seq_ev_is_ump(ev)) { |
1034 | if (count < sizeof(struct snd_seq_ump_event)) |
1035 | break; |
1036 | if (copy_from_user(to: (char *)ev + len, from: buf + len, |
1037 | n: sizeof(struct snd_seq_ump_event) - len)) { |
1038 | err = -EFAULT; |
1039 | break; |
1040 | } |
1041 | len = sizeof(struct snd_seq_ump_event); |
1042 | } |
1043 | |
1044 | ev->source.client = client->number; /* fill in client number */ |
1045 | /* Check for extension data length */ |
1046 | if (check_event_type_and_length(ev)) { |
1047 | err = -EINVAL; |
1048 | break; |
1049 | } |
1050 | |
1051 | if (!event_is_compatible(client, ev)) { |
1052 | err = -EINVAL; |
1053 | break; |
1054 | } |
1055 | |
1056 | /* check for special events */ |
1057 | if (!snd_seq_ev_is_ump(ev)) { |
1058 | if (ev->type == SNDRV_SEQ_EVENT_NONE) |
1059 | goto __skip_event; |
1060 | else if (snd_seq_ev_is_reserved(ev)) { |
1061 | err = -EINVAL; |
1062 | break; |
1063 | } |
1064 | } |
1065 | |
1066 | if (snd_seq_ev_is_variable(ev)) { |
1067 | int extlen = ev->data.ext.len & ~SNDRV_SEQ_EXT_MASK; |
1068 | if ((size_t)(extlen + len) > count) { |
1069 | /* back out, will get an error this time or next */ |
1070 | err = -EINVAL; |
1071 | break; |
1072 | } |
1073 | /* set user space pointer */ |
1074 | ev->data.ext.len = extlen | SNDRV_SEQ_EXT_USRPTR; |
1075 | ev->data.ext.ptr = (char __force *)buf + len; |
1076 | len += extlen; /* increment data length */ |
1077 | } else { |
1078 | #ifdef CONFIG_COMPAT |
1079 | if (client->convert32 && snd_seq_ev_is_varusr(ev)) |
1080 | ev->data.ext.ptr = |
1081 | (void __force *)compat_ptr(uptr: ev->data.raw32.d[1]); |
1082 | #endif |
1083 | } |
1084 | |
1085 | /* ok, enqueue it */ |
1086 | err = snd_seq_client_enqueue_event(client, event: ev, file, |
1087 | blocking: !(file->f_flags & O_NONBLOCK), |
1088 | atomic: 0, hop: 0, mutexp: &client->ioctl_mutex); |
1089 | if (err < 0) |
1090 | break; |
1091 | handled++; |
1092 | |
1093 | __skip_event: |
1094 | /* Update pointers and counts */ |
1095 | count -= len; |
1096 | buf += len; |
1097 | written += len; |
1098 | |
1099 | /* let's have a coffee break if too many events are queued */ |
1100 | if (++handled >= 200) { |
1101 | mutex_unlock(lock: &client->ioctl_mutex); |
1102 | goto repeat; |
1103 | } |
1104 | } |
1105 | |
1106 | out: |
1107 | mutex_unlock(lock: &client->ioctl_mutex); |
1108 | return written ? written : err; |
1109 | } |
1110 | |
1111 | |
1112 | /* |
1113 | * handle polling |
1114 | */ |
1115 | static __poll_t snd_seq_poll(struct file *file, poll_table * wait) |
1116 | { |
1117 | struct snd_seq_client *client = file->private_data; |
1118 | __poll_t mask = 0; |
1119 | |
1120 | /* check client structures are in place */ |
1121 | if (snd_BUG_ON(!client)) |
1122 | return EPOLLERR; |
1123 | |
1124 | if ((snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT) && |
1125 | client->data.user.fifo) { |
1126 | |
1127 | /* check if data is available in the outqueue */ |
1128 | if (snd_seq_fifo_poll_wait(f: client->data.user.fifo, file, wait)) |
1129 | mask |= EPOLLIN | EPOLLRDNORM; |
1130 | } |
1131 | |
1132 | if (snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT) { |
1133 | |
1134 | /* check if data is available in the pool */ |
1135 | if (!snd_seq_write_pool_allocated(client) || |
1136 | snd_seq_pool_poll_wait(pool: client->pool, file, wait)) |
1137 | mask |= EPOLLOUT | EPOLLWRNORM; |
1138 | } |
1139 | |
1140 | return mask; |
1141 | } |
1142 | |
1143 | |
1144 | /*-----------------------------------------------------*/ |
1145 | |
1146 | static int snd_seq_ioctl_pversion(struct snd_seq_client *client, void *arg) |
1147 | { |
1148 | int *pversion = arg; |
1149 | |
1150 | *pversion = SNDRV_SEQ_VERSION; |
1151 | return 0; |
1152 | } |
1153 | |
1154 | static int snd_seq_ioctl_user_pversion(struct snd_seq_client *client, void *arg) |
1155 | { |
1156 | client->user_pversion = *(unsigned int *)arg; |
1157 | return 0; |
1158 | } |
1159 | |
1160 | static int snd_seq_ioctl_client_id(struct snd_seq_client *client, void *arg) |
1161 | { |
1162 | int *client_id = arg; |
1163 | |
1164 | *client_id = client->number; |
1165 | return 0; |
1166 | } |
1167 | |
1168 | /* SYSTEM_INFO ioctl() */ |
1169 | static int snd_seq_ioctl_system_info(struct snd_seq_client *client, void *arg) |
1170 | { |
1171 | struct snd_seq_system_info *info = arg; |
1172 | |
1173 | memset(info, 0, sizeof(*info)); |
1174 | /* fill the info fields */ |
1175 | info->queues = SNDRV_SEQ_MAX_QUEUES; |
1176 | info->clients = SNDRV_SEQ_MAX_CLIENTS; |
1177 | info->ports = SNDRV_SEQ_MAX_PORTS; |
1178 | info->channels = 256; /* fixed limit */ |
1179 | info->cur_clients = client_usage.cur; |
1180 | info->cur_queues = snd_seq_queue_get_cur_queues(); |
1181 | |
1182 | return 0; |
1183 | } |
1184 | |
1185 | |
1186 | /* RUNNING_MODE ioctl() */ |
1187 | static int snd_seq_ioctl_running_mode(struct snd_seq_client *client, void *arg) |
1188 | { |
1189 | struct snd_seq_running_info *info = arg; |
1190 | struct snd_seq_client *cptr; |
1191 | int err = 0; |
1192 | |
1193 | /* requested client number */ |
1194 | cptr = snd_seq_client_use_ptr(clientid: info->client); |
1195 | if (cptr == NULL) |
1196 | return -ENOENT; /* don't change !!! */ |
1197 | |
1198 | #ifdef SNDRV_BIG_ENDIAN |
1199 | if (!info->big_endian) { |
1200 | err = -EINVAL; |
1201 | goto __err; |
1202 | } |
1203 | #else |
1204 | if (info->big_endian) { |
1205 | err = -EINVAL; |
1206 | goto __err; |
1207 | } |
1208 | |
1209 | #endif |
1210 | if (info->cpu_mode > sizeof(long)) { |
1211 | err = -EINVAL; |
1212 | goto __err; |
1213 | } |
1214 | cptr->convert32 = (info->cpu_mode < sizeof(long)); |
1215 | __err: |
1216 | snd_seq_client_unlock(cptr); |
1217 | return err; |
1218 | } |
1219 | |
1220 | /* CLIENT_INFO ioctl() */ |
1221 | static void get_client_info(struct snd_seq_client *cptr, |
1222 | struct snd_seq_client_info *info) |
1223 | { |
1224 | info->client = cptr->number; |
1225 | |
1226 | /* fill the info fields */ |
1227 | info->type = cptr->type; |
1228 | strcpy(p: info->name, q: cptr->name); |
1229 | info->filter = cptr->filter; |
1230 | info->event_lost = cptr->event_lost; |
1231 | memcpy(info->event_filter, cptr->event_filter, 32); |
1232 | info->group_filter = cptr->group_filter; |
1233 | info->num_ports = cptr->num_ports; |
1234 | |
1235 | if (cptr->type == USER_CLIENT) |
1236 | info->pid = pid_vnr(pid: cptr->data.user.owner); |
1237 | else |
1238 | info->pid = -1; |
1239 | |
1240 | if (cptr->type == KERNEL_CLIENT) |
1241 | info->card = cptr->data.kernel.card ? cptr->data.kernel.card->number : -1; |
1242 | else |
1243 | info->card = -1; |
1244 | |
1245 | info->midi_version = cptr->midi_version; |
1246 | memset(info->reserved, 0, sizeof(info->reserved)); |
1247 | } |
1248 | |
1249 | static int snd_seq_ioctl_get_client_info(struct snd_seq_client *client, |
1250 | void *arg) |
1251 | { |
1252 | struct snd_seq_client_info *client_info = arg; |
1253 | struct snd_seq_client *cptr; |
1254 | |
1255 | /* requested client number */ |
1256 | cptr = snd_seq_client_use_ptr(clientid: client_info->client); |
1257 | if (cptr == NULL) |
1258 | return -ENOENT; /* don't change !!! */ |
1259 | |
1260 | get_client_info(cptr, info: client_info); |
1261 | snd_seq_client_unlock(cptr); |
1262 | |
1263 | return 0; |
1264 | } |
1265 | |
1266 | |
1267 | /* CLIENT_INFO ioctl() */ |
1268 | static int snd_seq_ioctl_set_client_info(struct snd_seq_client *client, |
1269 | void *arg) |
1270 | { |
1271 | struct snd_seq_client_info *client_info = arg; |
1272 | |
1273 | /* it is not allowed to set the info fields for an another client */ |
1274 | if (client->number != client_info->client) |
1275 | return -EPERM; |
1276 | /* also client type must be set now */ |
1277 | if (client->type != client_info->type) |
1278 | return -EINVAL; |
1279 | |
1280 | /* check validity of midi_version field */ |
1281 | if (client->user_pversion >= SNDRV_PROTOCOL_VERSION(1, 0, 3) && |
1282 | client_info->midi_version > SNDRV_SEQ_CLIENT_UMP_MIDI_2_0) |
1283 | return -EINVAL; |
1284 | |
1285 | /* fill the info fields */ |
1286 | if (client_info->name[0]) |
1287 | strscpy(client->name, client_info->name, sizeof(client->name)); |
1288 | |
1289 | client->filter = client_info->filter; |
1290 | client->event_lost = client_info->event_lost; |
1291 | if (client->user_pversion >= SNDRV_PROTOCOL_VERSION(1, 0, 3)) |
1292 | client->midi_version = client_info->midi_version; |
1293 | memcpy(client->event_filter, client_info->event_filter, 32); |
1294 | client->group_filter = client_info->group_filter; |
1295 | return 0; |
1296 | } |
1297 | |
1298 | |
1299 | /* |
1300 | * CREATE PORT ioctl() |
1301 | */ |
1302 | static int snd_seq_ioctl_create_port(struct snd_seq_client *client, void *arg) |
1303 | { |
1304 | struct snd_seq_port_info *info = arg; |
1305 | struct snd_seq_client_port *port; |
1306 | struct snd_seq_port_callback *callback; |
1307 | int port_idx, err; |
1308 | |
1309 | /* it is not allowed to create the port for an another client */ |
1310 | if (info->addr.client != client->number) |
1311 | return -EPERM; |
1312 | if (client->type == USER_CLIENT && info->kernel) |
1313 | return -EINVAL; |
1314 | if ((info->capability & SNDRV_SEQ_PORT_CAP_UMP_ENDPOINT) && |
1315 | client->ump_endpoint_port >= 0) |
1316 | return -EBUSY; |
1317 | |
1318 | if (info->flags & SNDRV_SEQ_PORT_FLG_GIVEN_PORT) |
1319 | port_idx = info->addr.port; |
1320 | else |
1321 | port_idx = -1; |
1322 | if (port_idx >= SNDRV_SEQ_ADDRESS_UNKNOWN) |
1323 | return -EINVAL; |
1324 | err = snd_seq_create_port(client, port_index: port_idx, port_ret: &port); |
1325 | if (err < 0) |
1326 | return err; |
1327 | |
1328 | if (client->type == KERNEL_CLIENT) { |
1329 | callback = info->kernel; |
1330 | if (callback) { |
1331 | if (callback->owner) |
1332 | port->owner = callback->owner; |
1333 | port->private_data = callback->private_data; |
1334 | port->private_free = callback->private_free; |
1335 | port->event_input = callback->event_input; |
1336 | port->c_src.open = callback->subscribe; |
1337 | port->c_src.close = callback->unsubscribe; |
1338 | port->c_dest.open = callback->use; |
1339 | port->c_dest.close = callback->unuse; |
1340 | } |
1341 | } |
1342 | |
1343 | info->addr = port->addr; |
1344 | |
1345 | snd_seq_set_port_info(port, info); |
1346 | if (info->capability & SNDRV_SEQ_PORT_CAP_UMP_ENDPOINT) |
1347 | client->ump_endpoint_port = port->addr.port; |
1348 | snd_seq_system_client_ev_port_start(port->addr.client, port->addr.port); |
1349 | snd_seq_port_unlock(port); |
1350 | |
1351 | return 0; |
1352 | } |
1353 | |
1354 | /* |
1355 | * DELETE PORT ioctl() |
1356 | */ |
1357 | static int snd_seq_ioctl_delete_port(struct snd_seq_client *client, void *arg) |
1358 | { |
1359 | struct snd_seq_port_info *info = arg; |
1360 | int err; |
1361 | |
1362 | /* it is not allowed to remove the port for an another client */ |
1363 | if (info->addr.client != client->number) |
1364 | return -EPERM; |
1365 | |
1366 | err = snd_seq_delete_port(client, port: info->addr.port); |
1367 | if (err >= 0) { |
1368 | if (client->ump_endpoint_port == info->addr.port) |
1369 | client->ump_endpoint_port = -1; |
1370 | snd_seq_system_client_ev_port_exit(client->number, info->addr.port); |
1371 | } |
1372 | return err; |
1373 | } |
1374 | |
1375 | |
1376 | /* |
1377 | * GET_PORT_INFO ioctl() (on any client) |
1378 | */ |
1379 | static int snd_seq_ioctl_get_port_info(struct snd_seq_client *client, void *arg) |
1380 | { |
1381 | struct snd_seq_port_info *info = arg; |
1382 | struct snd_seq_client *cptr; |
1383 | struct snd_seq_client_port *port; |
1384 | |
1385 | cptr = snd_seq_client_use_ptr(clientid: info->addr.client); |
1386 | if (cptr == NULL) |
1387 | return -ENXIO; |
1388 | |
1389 | port = snd_seq_port_use_ptr(client: cptr, num: info->addr.port); |
1390 | if (port == NULL) { |
1391 | snd_seq_client_unlock(cptr); |
1392 | return -ENOENT; /* don't change */ |
1393 | } |
1394 | |
1395 | /* get port info */ |
1396 | snd_seq_get_port_info(port, info); |
1397 | snd_seq_port_unlock(port); |
1398 | snd_seq_client_unlock(cptr); |
1399 | |
1400 | return 0; |
1401 | } |
1402 | |
1403 | |
1404 | /* |
1405 | * SET_PORT_INFO ioctl() (only ports on this/own client) |
1406 | */ |
1407 | static int snd_seq_ioctl_set_port_info(struct snd_seq_client *client, void *arg) |
1408 | { |
1409 | struct snd_seq_port_info *info = arg; |
1410 | struct snd_seq_client_port *port; |
1411 | |
1412 | if (info->addr.client != client->number) /* only set our own ports ! */ |
1413 | return -EPERM; |
1414 | port = snd_seq_port_use_ptr(client, num: info->addr.port); |
1415 | if (port) { |
1416 | snd_seq_set_port_info(port, info); |
1417 | snd_seq_port_unlock(port); |
1418 | } |
1419 | return 0; |
1420 | } |
1421 | |
1422 | |
1423 | /* |
1424 | * port subscription (connection) |
1425 | */ |
1426 | #define PERM_RD (SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ) |
1427 | #define PERM_WR (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE) |
1428 | |
1429 | static int check_subscription_permission(struct snd_seq_client *client, |
1430 | struct snd_seq_client_port *sport, |
1431 | struct snd_seq_client_port *dport, |
1432 | struct snd_seq_port_subscribe *subs) |
1433 | { |
1434 | if (client->number != subs->sender.client && |
1435 | client->number != subs->dest.client) { |
1436 | /* connection by third client - check export permission */ |
1437 | if (check_port_perm(port: sport, SNDRV_SEQ_PORT_CAP_NO_EXPORT)) |
1438 | return -EPERM; |
1439 | if (check_port_perm(port: dport, SNDRV_SEQ_PORT_CAP_NO_EXPORT)) |
1440 | return -EPERM; |
1441 | } |
1442 | |
1443 | /* check read permission */ |
1444 | /* if sender or receiver is the subscribing client itself, |
1445 | * no permission check is necessary |
1446 | */ |
1447 | if (client->number != subs->sender.client) { |
1448 | if (! check_port_perm(port: sport, PERM_RD)) |
1449 | return -EPERM; |
1450 | } |
1451 | /* check write permission */ |
1452 | if (client->number != subs->dest.client) { |
1453 | if (! check_port_perm(port: dport, PERM_WR)) |
1454 | return -EPERM; |
1455 | } |
1456 | return 0; |
1457 | } |
1458 | |
1459 | /* |
1460 | * send an subscription notify event to user client: |
1461 | * client must be user client. |
1462 | */ |
1463 | int snd_seq_client_notify_subscription(int client, int port, |
1464 | struct snd_seq_port_subscribe *info, |
1465 | int evtype) |
1466 | { |
1467 | struct snd_seq_event event; |
1468 | |
1469 | memset(&event, 0, sizeof(event)); |
1470 | event.type = evtype; |
1471 | event.data.connect.dest = info->dest; |
1472 | event.data.connect.sender = info->sender; |
1473 | |
1474 | return snd_seq_system_notify(client, port, ev: &event); /* non-atomic */ |
1475 | } |
1476 | |
1477 | |
1478 | /* |
1479 | * add to port's subscription list IOCTL interface |
1480 | */ |
1481 | static int snd_seq_ioctl_subscribe_port(struct snd_seq_client *client, |
1482 | void *arg) |
1483 | { |
1484 | struct snd_seq_port_subscribe *subs = arg; |
1485 | int result = -EINVAL; |
1486 | struct snd_seq_client *receiver = NULL, *sender = NULL; |
1487 | struct snd_seq_client_port *sport = NULL, *dport = NULL; |
1488 | |
1489 | receiver = snd_seq_client_use_ptr(clientid: subs->dest.client); |
1490 | if (!receiver) |
1491 | goto __end; |
1492 | sender = snd_seq_client_use_ptr(clientid: subs->sender.client); |
1493 | if (!sender) |
1494 | goto __end; |
1495 | sport = snd_seq_port_use_ptr(client: sender, num: subs->sender.port); |
1496 | if (!sport) |
1497 | goto __end; |
1498 | dport = snd_seq_port_use_ptr(client: receiver, num: subs->dest.port); |
1499 | if (!dport) |
1500 | goto __end; |
1501 | |
1502 | result = check_subscription_permission(client, sport, dport, subs); |
1503 | if (result < 0) |
1504 | goto __end; |
1505 | |
1506 | /* connect them */ |
1507 | result = snd_seq_port_connect(caller: client, s: sender, sp: sport, d: receiver, dp: dport, info: subs); |
1508 | if (! result) /* broadcast announce */ |
1509 | snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, port: 0, |
1510 | info: subs, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED); |
1511 | __end: |
1512 | if (sport) |
1513 | snd_seq_port_unlock(sport); |
1514 | if (dport) |
1515 | snd_seq_port_unlock(dport); |
1516 | if (sender) |
1517 | snd_seq_client_unlock(sender); |
1518 | if (receiver) |
1519 | snd_seq_client_unlock(receiver); |
1520 | return result; |
1521 | } |
1522 | |
1523 | |
1524 | /* |
1525 | * remove from port's subscription list |
1526 | */ |
1527 | static int snd_seq_ioctl_unsubscribe_port(struct snd_seq_client *client, |
1528 | void *arg) |
1529 | { |
1530 | struct snd_seq_port_subscribe *subs = arg; |
1531 | int result = -ENXIO; |
1532 | struct snd_seq_client *receiver = NULL, *sender = NULL; |
1533 | struct snd_seq_client_port *sport = NULL, *dport = NULL; |
1534 | |
1535 | receiver = snd_seq_client_use_ptr(clientid: subs->dest.client); |
1536 | if (!receiver) |
1537 | goto __end; |
1538 | sender = snd_seq_client_use_ptr(clientid: subs->sender.client); |
1539 | if (!sender) |
1540 | goto __end; |
1541 | sport = snd_seq_port_use_ptr(client: sender, num: subs->sender.port); |
1542 | if (!sport) |
1543 | goto __end; |
1544 | dport = snd_seq_port_use_ptr(client: receiver, num: subs->dest.port); |
1545 | if (!dport) |
1546 | goto __end; |
1547 | |
1548 | result = check_subscription_permission(client, sport, dport, subs); |
1549 | if (result < 0) |
1550 | goto __end; |
1551 | |
1552 | result = snd_seq_port_disconnect(caller: client, s: sender, sp: sport, d: receiver, dp: dport, info: subs); |
1553 | if (! result) /* broadcast announce */ |
1554 | snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, port: 0, |
1555 | info: subs, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED); |
1556 | __end: |
1557 | if (sport) |
1558 | snd_seq_port_unlock(sport); |
1559 | if (dport) |
1560 | snd_seq_port_unlock(dport); |
1561 | if (sender) |
1562 | snd_seq_client_unlock(sender); |
1563 | if (receiver) |
1564 | snd_seq_client_unlock(receiver); |
1565 | return result; |
1566 | } |
1567 | |
1568 | |
1569 | /* CREATE_QUEUE ioctl() */ |
1570 | static int snd_seq_ioctl_create_queue(struct snd_seq_client *client, void *arg) |
1571 | { |
1572 | struct snd_seq_queue_info *info = arg; |
1573 | struct snd_seq_queue *q; |
1574 | |
1575 | q = snd_seq_queue_alloc(client: client->number, locked: info->locked, flags: info->flags); |
1576 | if (IS_ERR(ptr: q)) |
1577 | return PTR_ERR(ptr: q); |
1578 | |
1579 | info->queue = q->queue; |
1580 | info->locked = q->locked; |
1581 | info->owner = q->owner; |
1582 | |
1583 | /* set queue name */ |
1584 | if (!info->name[0]) |
1585 | snprintf(buf: info->name, size: sizeof(info->name), fmt: "Queue-%d" , q->queue); |
1586 | strscpy(q->name, info->name, sizeof(q->name)); |
1587 | snd_use_lock_free(&q->use_lock); |
1588 | |
1589 | return 0; |
1590 | } |
1591 | |
1592 | /* DELETE_QUEUE ioctl() */ |
1593 | static int snd_seq_ioctl_delete_queue(struct snd_seq_client *client, void *arg) |
1594 | { |
1595 | struct snd_seq_queue_info *info = arg; |
1596 | |
1597 | return snd_seq_queue_delete(client: client->number, queueid: info->queue); |
1598 | } |
1599 | |
1600 | /* GET_QUEUE_INFO ioctl() */ |
1601 | static int snd_seq_ioctl_get_queue_info(struct snd_seq_client *client, |
1602 | void *arg) |
1603 | { |
1604 | struct snd_seq_queue_info *info = arg; |
1605 | struct snd_seq_queue *q; |
1606 | |
1607 | q = queueptr(queueid: info->queue); |
1608 | if (q == NULL) |
1609 | return -EINVAL; |
1610 | |
1611 | memset(info, 0, sizeof(*info)); |
1612 | info->queue = q->queue; |
1613 | info->owner = q->owner; |
1614 | info->locked = q->locked; |
1615 | strscpy(info->name, q->name, sizeof(info->name)); |
1616 | queuefree(q); |
1617 | |
1618 | return 0; |
1619 | } |
1620 | |
1621 | /* SET_QUEUE_INFO ioctl() */ |
1622 | static int snd_seq_ioctl_set_queue_info(struct snd_seq_client *client, |
1623 | void *arg) |
1624 | { |
1625 | struct snd_seq_queue_info *info = arg; |
1626 | struct snd_seq_queue *q; |
1627 | |
1628 | if (info->owner != client->number) |
1629 | return -EINVAL; |
1630 | |
1631 | /* change owner/locked permission */ |
1632 | if (snd_seq_queue_check_access(queueid: info->queue, client: client->number)) { |
1633 | if (snd_seq_queue_set_owner(queueid: info->queue, client: client->number, locked: info->locked) < 0) |
1634 | return -EPERM; |
1635 | if (info->locked) |
1636 | snd_seq_queue_use(queueid: info->queue, client: client->number, use: 1); |
1637 | } else { |
1638 | return -EPERM; |
1639 | } |
1640 | |
1641 | q = queueptr(queueid: info->queue); |
1642 | if (! q) |
1643 | return -EINVAL; |
1644 | if (q->owner != client->number) { |
1645 | queuefree(q); |
1646 | return -EPERM; |
1647 | } |
1648 | strscpy(q->name, info->name, sizeof(q->name)); |
1649 | queuefree(q); |
1650 | |
1651 | return 0; |
1652 | } |
1653 | |
1654 | /* GET_NAMED_QUEUE ioctl() */ |
1655 | static int snd_seq_ioctl_get_named_queue(struct snd_seq_client *client, |
1656 | void *arg) |
1657 | { |
1658 | struct snd_seq_queue_info *info = arg; |
1659 | struct snd_seq_queue *q; |
1660 | |
1661 | q = snd_seq_queue_find_name(name: info->name); |
1662 | if (q == NULL) |
1663 | return -EINVAL; |
1664 | info->queue = q->queue; |
1665 | info->owner = q->owner; |
1666 | info->locked = q->locked; |
1667 | queuefree(q); |
1668 | |
1669 | return 0; |
1670 | } |
1671 | |
1672 | /* GET_QUEUE_STATUS ioctl() */ |
1673 | static int snd_seq_ioctl_get_queue_status(struct snd_seq_client *client, |
1674 | void *arg) |
1675 | { |
1676 | struct snd_seq_queue_status *status = arg; |
1677 | struct snd_seq_queue *queue; |
1678 | struct snd_seq_timer *tmr; |
1679 | |
1680 | queue = queueptr(queueid: status->queue); |
1681 | if (queue == NULL) |
1682 | return -EINVAL; |
1683 | memset(status, 0, sizeof(*status)); |
1684 | status->queue = queue->queue; |
1685 | |
1686 | tmr = queue->timer; |
1687 | status->events = queue->tickq->cells + queue->timeq->cells; |
1688 | |
1689 | status->time = snd_seq_timer_get_cur_time(tmr, adjust_ktime: true); |
1690 | status->tick = snd_seq_timer_get_cur_tick(tmr); |
1691 | |
1692 | status->running = tmr->running; |
1693 | |
1694 | status->flags = queue->flags; |
1695 | queuefree(queue); |
1696 | |
1697 | return 0; |
1698 | } |
1699 | |
1700 | |
1701 | /* GET_QUEUE_TEMPO ioctl() */ |
1702 | static int snd_seq_ioctl_get_queue_tempo(struct snd_seq_client *client, |
1703 | void *arg) |
1704 | { |
1705 | struct snd_seq_queue_tempo *tempo = arg; |
1706 | struct snd_seq_queue *queue; |
1707 | struct snd_seq_timer *tmr; |
1708 | |
1709 | queue = queueptr(queueid: tempo->queue); |
1710 | if (queue == NULL) |
1711 | return -EINVAL; |
1712 | memset(tempo, 0, sizeof(*tempo)); |
1713 | tempo->queue = queue->queue; |
1714 | |
1715 | tmr = queue->timer; |
1716 | |
1717 | tempo->tempo = tmr->tempo; |
1718 | tempo->ppq = tmr->ppq; |
1719 | tempo->skew_value = tmr->skew; |
1720 | tempo->skew_base = tmr->skew_base; |
1721 | queuefree(queue); |
1722 | |
1723 | return 0; |
1724 | } |
1725 | |
1726 | |
1727 | /* SET_QUEUE_TEMPO ioctl() */ |
1728 | int snd_seq_set_queue_tempo(int client, struct snd_seq_queue_tempo *tempo) |
1729 | { |
1730 | if (!snd_seq_queue_check_access(queueid: tempo->queue, client)) |
1731 | return -EPERM; |
1732 | return snd_seq_queue_timer_set_tempo(queueid: tempo->queue, client, info: tempo); |
1733 | } |
1734 | EXPORT_SYMBOL(snd_seq_set_queue_tempo); |
1735 | |
1736 | static int snd_seq_ioctl_set_queue_tempo(struct snd_seq_client *client, |
1737 | void *arg) |
1738 | { |
1739 | struct snd_seq_queue_tempo *tempo = arg; |
1740 | int result; |
1741 | |
1742 | result = snd_seq_set_queue_tempo(client->number, tempo); |
1743 | return result < 0 ? result : 0; |
1744 | } |
1745 | |
1746 | |
1747 | /* GET_QUEUE_TIMER ioctl() */ |
1748 | static int snd_seq_ioctl_get_queue_timer(struct snd_seq_client *client, |
1749 | void *arg) |
1750 | { |
1751 | struct snd_seq_queue_timer *timer = arg; |
1752 | struct snd_seq_queue *queue; |
1753 | struct snd_seq_timer *tmr; |
1754 | |
1755 | queue = queueptr(queueid: timer->queue); |
1756 | if (queue == NULL) |
1757 | return -EINVAL; |
1758 | |
1759 | mutex_lock(&queue->timer_mutex); |
1760 | tmr = queue->timer; |
1761 | memset(timer, 0, sizeof(*timer)); |
1762 | timer->queue = queue->queue; |
1763 | |
1764 | timer->type = tmr->type; |
1765 | if (tmr->type == SNDRV_SEQ_TIMER_ALSA) { |
1766 | timer->u.alsa.id = tmr->alsa_id; |
1767 | timer->u.alsa.resolution = tmr->preferred_resolution; |
1768 | } |
1769 | mutex_unlock(lock: &queue->timer_mutex); |
1770 | queuefree(queue); |
1771 | |
1772 | return 0; |
1773 | } |
1774 | |
1775 | |
1776 | /* SET_QUEUE_TIMER ioctl() */ |
1777 | static int snd_seq_ioctl_set_queue_timer(struct snd_seq_client *client, |
1778 | void *arg) |
1779 | { |
1780 | struct snd_seq_queue_timer *timer = arg; |
1781 | int result = 0; |
1782 | |
1783 | if (timer->type != SNDRV_SEQ_TIMER_ALSA) |
1784 | return -EINVAL; |
1785 | |
1786 | if (snd_seq_queue_check_access(queueid: timer->queue, client: client->number)) { |
1787 | struct snd_seq_queue *q; |
1788 | struct snd_seq_timer *tmr; |
1789 | |
1790 | q = queueptr(queueid: timer->queue); |
1791 | if (q == NULL) |
1792 | return -ENXIO; |
1793 | mutex_lock(&q->timer_mutex); |
1794 | tmr = q->timer; |
1795 | snd_seq_queue_timer_close(queueid: timer->queue); |
1796 | tmr->type = timer->type; |
1797 | if (tmr->type == SNDRV_SEQ_TIMER_ALSA) { |
1798 | tmr->alsa_id = timer->u.alsa.id; |
1799 | tmr->preferred_resolution = timer->u.alsa.resolution; |
1800 | } |
1801 | result = snd_seq_queue_timer_open(queueid: timer->queue); |
1802 | mutex_unlock(lock: &q->timer_mutex); |
1803 | queuefree(q); |
1804 | } else { |
1805 | return -EPERM; |
1806 | } |
1807 | |
1808 | return result; |
1809 | } |
1810 | |
1811 | |
1812 | /* GET_QUEUE_CLIENT ioctl() */ |
1813 | static int snd_seq_ioctl_get_queue_client(struct snd_seq_client *client, |
1814 | void *arg) |
1815 | { |
1816 | struct snd_seq_queue_client *info = arg; |
1817 | int used; |
1818 | |
1819 | used = snd_seq_queue_is_used(queueid: info->queue, client: client->number); |
1820 | if (used < 0) |
1821 | return -EINVAL; |
1822 | info->used = used; |
1823 | info->client = client->number; |
1824 | |
1825 | return 0; |
1826 | } |
1827 | |
1828 | |
1829 | /* SET_QUEUE_CLIENT ioctl() */ |
1830 | static int snd_seq_ioctl_set_queue_client(struct snd_seq_client *client, |
1831 | void *arg) |
1832 | { |
1833 | struct snd_seq_queue_client *info = arg; |
1834 | int err; |
1835 | |
1836 | if (info->used >= 0) { |
1837 | err = snd_seq_queue_use(queueid: info->queue, client: client->number, use: info->used); |
1838 | if (err < 0) |
1839 | return err; |
1840 | } |
1841 | |
1842 | return snd_seq_ioctl_get_queue_client(client, arg); |
1843 | } |
1844 | |
1845 | |
1846 | /* GET_CLIENT_POOL ioctl() */ |
1847 | static int snd_seq_ioctl_get_client_pool(struct snd_seq_client *client, |
1848 | void *arg) |
1849 | { |
1850 | struct snd_seq_client_pool *info = arg; |
1851 | struct snd_seq_client *cptr; |
1852 | |
1853 | cptr = snd_seq_client_use_ptr(clientid: info->client); |
1854 | if (cptr == NULL) |
1855 | return -ENOENT; |
1856 | memset(info, 0, sizeof(*info)); |
1857 | info->client = cptr->number; |
1858 | info->output_pool = cptr->pool->size; |
1859 | info->output_room = cptr->pool->room; |
1860 | info->output_free = info->output_pool; |
1861 | info->output_free = snd_seq_unused_cells(pool: cptr->pool); |
1862 | if (cptr->type == USER_CLIENT) { |
1863 | info->input_pool = cptr->data.user.fifo_pool_size; |
1864 | info->input_free = info->input_pool; |
1865 | info->input_free = snd_seq_fifo_unused_cells(f: cptr->data.user.fifo); |
1866 | } else { |
1867 | info->input_pool = 0; |
1868 | info->input_free = 0; |
1869 | } |
1870 | snd_seq_client_unlock(cptr); |
1871 | |
1872 | return 0; |
1873 | } |
1874 | |
1875 | /* SET_CLIENT_POOL ioctl() */ |
1876 | static int snd_seq_ioctl_set_client_pool(struct snd_seq_client *client, |
1877 | void *arg) |
1878 | { |
1879 | struct snd_seq_client_pool *info = arg; |
1880 | int rc; |
1881 | |
1882 | if (client->number != info->client) |
1883 | return -EINVAL; /* can't change other clients */ |
1884 | |
1885 | if (info->output_pool >= 1 && info->output_pool <= SNDRV_SEQ_MAX_EVENTS && |
1886 | (! snd_seq_write_pool_allocated(client) || |
1887 | info->output_pool != client->pool->size)) { |
1888 | if (snd_seq_write_pool_allocated(client)) { |
1889 | /* is the pool in use? */ |
1890 | if (atomic_read(v: &client->pool->counter)) |
1891 | return -EBUSY; |
1892 | /* remove all existing cells */ |
1893 | snd_seq_pool_mark_closing(pool: client->pool); |
1894 | snd_seq_pool_done(pool: client->pool); |
1895 | } |
1896 | client->pool->size = info->output_pool; |
1897 | rc = snd_seq_pool_init(pool: client->pool); |
1898 | if (rc < 0) |
1899 | return rc; |
1900 | } |
1901 | if (client->type == USER_CLIENT && client->data.user.fifo != NULL && |
1902 | info->input_pool >= 1 && |
1903 | info->input_pool <= SNDRV_SEQ_MAX_CLIENT_EVENTS && |
1904 | info->input_pool != client->data.user.fifo_pool_size) { |
1905 | /* change pool size */ |
1906 | rc = snd_seq_fifo_resize(f: client->data.user.fifo, poolsize: info->input_pool); |
1907 | if (rc < 0) |
1908 | return rc; |
1909 | client->data.user.fifo_pool_size = info->input_pool; |
1910 | } |
1911 | if (info->output_room >= 1 && |
1912 | info->output_room <= client->pool->size) { |
1913 | client->pool->room = info->output_room; |
1914 | } |
1915 | |
1916 | return snd_seq_ioctl_get_client_pool(client, arg); |
1917 | } |
1918 | |
1919 | |
1920 | /* REMOVE_EVENTS ioctl() */ |
1921 | static int snd_seq_ioctl_remove_events(struct snd_seq_client *client, |
1922 | void *arg) |
1923 | { |
1924 | struct snd_seq_remove_events *info = arg; |
1925 | |
1926 | /* |
1927 | * Input mostly not implemented XXX. |
1928 | */ |
1929 | if (info->remove_mode & SNDRV_SEQ_REMOVE_INPUT) { |
1930 | /* |
1931 | * No restrictions so for a user client we can clear |
1932 | * the whole fifo |
1933 | */ |
1934 | if (client->type == USER_CLIENT && client->data.user.fifo) |
1935 | snd_seq_fifo_clear(f: client->data.user.fifo); |
1936 | } |
1937 | |
1938 | if (info->remove_mode & SNDRV_SEQ_REMOVE_OUTPUT) |
1939 | snd_seq_queue_remove_cells(client: client->number, info); |
1940 | |
1941 | return 0; |
1942 | } |
1943 | |
1944 | |
1945 | /* |
1946 | * get subscription info |
1947 | */ |
1948 | static int snd_seq_ioctl_get_subscription(struct snd_seq_client *client, |
1949 | void *arg) |
1950 | { |
1951 | struct snd_seq_port_subscribe *subs = arg; |
1952 | int result; |
1953 | struct snd_seq_client *sender = NULL; |
1954 | struct snd_seq_client_port *sport = NULL; |
1955 | |
1956 | result = -EINVAL; |
1957 | sender = snd_seq_client_use_ptr(clientid: subs->sender.client); |
1958 | if (!sender) |
1959 | goto __end; |
1960 | sport = snd_seq_port_use_ptr(client: sender, num: subs->sender.port); |
1961 | if (!sport) |
1962 | goto __end; |
1963 | result = snd_seq_port_get_subscription(src_grp: &sport->c_src, dest_addr: &subs->dest, |
1964 | subs); |
1965 | __end: |
1966 | if (sport) |
1967 | snd_seq_port_unlock(sport); |
1968 | if (sender) |
1969 | snd_seq_client_unlock(sender); |
1970 | |
1971 | return result; |
1972 | } |
1973 | |
1974 | |
1975 | /* |
1976 | * get subscription info - check only its presence |
1977 | */ |
1978 | static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, void *arg) |
1979 | { |
1980 | struct snd_seq_query_subs *subs = arg; |
1981 | int result = -ENXIO; |
1982 | struct snd_seq_client *cptr = NULL; |
1983 | struct snd_seq_client_port *port = NULL; |
1984 | struct snd_seq_port_subs_info *group; |
1985 | struct list_head *p; |
1986 | int i; |
1987 | |
1988 | cptr = snd_seq_client_use_ptr(clientid: subs->root.client); |
1989 | if (!cptr) |
1990 | goto __end; |
1991 | port = snd_seq_port_use_ptr(client: cptr, num: subs->root.port); |
1992 | if (!port) |
1993 | goto __end; |
1994 | |
1995 | switch (subs->type) { |
1996 | case SNDRV_SEQ_QUERY_SUBS_READ: |
1997 | group = &port->c_src; |
1998 | break; |
1999 | case SNDRV_SEQ_QUERY_SUBS_WRITE: |
2000 | group = &port->c_dest; |
2001 | break; |
2002 | default: |
2003 | goto __end; |
2004 | } |
2005 | |
2006 | down_read(sem: &group->list_mutex); |
2007 | /* search for the subscriber */ |
2008 | subs->num_subs = group->count; |
2009 | i = 0; |
2010 | result = -ENOENT; |
2011 | list_for_each(p, &group->list_head) { |
2012 | if (i++ == subs->index) { |
2013 | /* found! */ |
2014 | struct snd_seq_subscribers *s; |
2015 | if (subs->type == SNDRV_SEQ_QUERY_SUBS_READ) { |
2016 | s = list_entry(p, struct snd_seq_subscribers, src_list); |
2017 | subs->addr = s->info.dest; |
2018 | } else { |
2019 | s = list_entry(p, struct snd_seq_subscribers, dest_list); |
2020 | subs->addr = s->info.sender; |
2021 | } |
2022 | subs->flags = s->info.flags; |
2023 | subs->queue = s->info.queue; |
2024 | result = 0; |
2025 | break; |
2026 | } |
2027 | } |
2028 | up_read(sem: &group->list_mutex); |
2029 | |
2030 | __end: |
2031 | if (port) |
2032 | snd_seq_port_unlock(port); |
2033 | if (cptr) |
2034 | snd_seq_client_unlock(cptr); |
2035 | |
2036 | return result; |
2037 | } |
2038 | |
2039 | |
2040 | /* |
2041 | * query next client |
2042 | */ |
2043 | static int snd_seq_ioctl_query_next_client(struct snd_seq_client *client, |
2044 | void *arg) |
2045 | { |
2046 | struct snd_seq_client_info *info = arg; |
2047 | struct snd_seq_client *cptr = NULL; |
2048 | |
2049 | /* search for next client */ |
2050 | if (info->client < INT_MAX) |
2051 | info->client++; |
2052 | if (info->client < 0) |
2053 | info->client = 0; |
2054 | for (; info->client < SNDRV_SEQ_MAX_CLIENTS; info->client++) { |
2055 | cptr = snd_seq_client_use_ptr(clientid: info->client); |
2056 | if (cptr) |
2057 | break; /* found */ |
2058 | } |
2059 | if (cptr == NULL) |
2060 | return -ENOENT; |
2061 | |
2062 | get_client_info(cptr, info); |
2063 | snd_seq_client_unlock(cptr); |
2064 | |
2065 | return 0; |
2066 | } |
2067 | |
2068 | /* |
2069 | * query next port |
2070 | */ |
2071 | static int snd_seq_ioctl_query_next_port(struct snd_seq_client *client, |
2072 | void *arg) |
2073 | { |
2074 | struct snd_seq_port_info *info = arg; |
2075 | struct snd_seq_client *cptr; |
2076 | struct snd_seq_client_port *port = NULL; |
2077 | |
2078 | cptr = snd_seq_client_use_ptr(clientid: info->addr.client); |
2079 | if (cptr == NULL) |
2080 | return -ENXIO; |
2081 | |
2082 | /* search for next port */ |
2083 | info->addr.port++; |
2084 | port = snd_seq_port_query_nearest(client: cptr, pinfo: info); |
2085 | if (port == NULL) { |
2086 | snd_seq_client_unlock(cptr); |
2087 | return -ENOENT; |
2088 | } |
2089 | |
2090 | /* get port info */ |
2091 | info->addr = port->addr; |
2092 | snd_seq_get_port_info(port, info); |
2093 | snd_seq_port_unlock(port); |
2094 | snd_seq_client_unlock(cptr); |
2095 | |
2096 | return 0; |
2097 | } |
2098 | |
2099 | #if IS_ENABLED(CONFIG_SND_SEQ_UMP) |
2100 | #define NUM_UMP_INFOS (SNDRV_UMP_MAX_BLOCKS + 1) |
2101 | |
2102 | static void free_ump_info(struct snd_seq_client *client) |
2103 | { |
2104 | int i; |
2105 | |
2106 | if (!client->ump_info) |
2107 | return; |
2108 | for (i = 0; i < NUM_UMP_INFOS; i++) |
2109 | kfree(objp: client->ump_info[i]); |
2110 | kfree(objp: client->ump_info); |
2111 | client->ump_info = NULL; |
2112 | } |
2113 | |
2114 | static void terminate_ump_info_strings(void *p, int type) |
2115 | { |
2116 | if (type == SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT) { |
2117 | struct snd_ump_endpoint_info *ep = p; |
2118 | ep->name[sizeof(ep->name) - 1] = 0; |
2119 | } else { |
2120 | struct snd_ump_block_info *bp = p; |
2121 | bp->name[sizeof(bp->name) - 1] = 0; |
2122 | } |
2123 | } |
2124 | |
2125 | #ifdef CONFIG_SND_PROC_FS |
2126 | static void dump_ump_info(struct snd_info_buffer *buffer, |
2127 | struct snd_seq_client *client) |
2128 | { |
2129 | struct snd_ump_endpoint_info *ep; |
2130 | struct snd_ump_block_info *bp; |
2131 | int i; |
2132 | |
2133 | if (!client->ump_info) |
2134 | return; |
2135 | ep = client->ump_info[SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT]; |
2136 | if (ep && *ep->name) |
2137 | snd_iprintf(buffer, " UMP Endpoint: \"%s\"\n" , ep->name); |
2138 | for (i = 0; i < SNDRV_UMP_MAX_BLOCKS; i++) { |
2139 | bp = client->ump_info[i + 1]; |
2140 | if (bp && *bp->name) { |
2141 | snd_iprintf(buffer, " UMP Block %d: \"%s\" [%s]\n" , |
2142 | i, bp->name, |
2143 | bp->active ? "Active" : "Inactive" ); |
2144 | snd_iprintf(buffer, " Groups: %d-%d\n" , |
2145 | bp->first_group + 1, |
2146 | bp->first_group + bp->num_groups); |
2147 | } |
2148 | } |
2149 | } |
2150 | #endif |
2151 | |
2152 | /* UMP-specific ioctls -- called directly without data copy */ |
2153 | static int snd_seq_ioctl_client_ump_info(struct snd_seq_client *caller, |
2154 | unsigned int cmd, |
2155 | unsigned long arg) |
2156 | { |
2157 | struct snd_seq_client_ump_info __user *argp = |
2158 | (struct snd_seq_client_ump_info __user *)arg; |
2159 | struct snd_seq_client *cptr; |
2160 | int client, type, err = 0; |
2161 | size_t size; |
2162 | void *p; |
2163 | |
2164 | if (get_user(client, &argp->client) || get_user(type, &argp->type)) |
2165 | return -EFAULT; |
2166 | if (cmd == SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO && |
2167 | caller->number != client) |
2168 | return -EPERM; |
2169 | if (type < 0 || type >= NUM_UMP_INFOS) |
2170 | return -EINVAL; |
2171 | if (type == SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT) |
2172 | size = sizeof(struct snd_ump_endpoint_info); |
2173 | else |
2174 | size = sizeof(struct snd_ump_block_info); |
2175 | cptr = snd_seq_client_use_ptr(clientid: client); |
2176 | if (!cptr) |
2177 | return -ENOENT; |
2178 | |
2179 | mutex_lock(&cptr->ioctl_mutex); |
2180 | if (!cptr->midi_version) { |
2181 | err = -EBADFD; |
2182 | goto error; |
2183 | } |
2184 | |
2185 | if (cmd == SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO) { |
2186 | if (!cptr->ump_info) |
2187 | p = NULL; |
2188 | else |
2189 | p = cptr->ump_info[type]; |
2190 | if (!p) { |
2191 | err = -ENODEV; |
2192 | goto error; |
2193 | } |
2194 | if (copy_to_user(to: argp->info, from: p, n: size)) { |
2195 | err = -EFAULT; |
2196 | goto error; |
2197 | } |
2198 | } else { |
2199 | if (cptr->type != USER_CLIENT) { |
2200 | err = -EBADFD; |
2201 | goto error; |
2202 | } |
2203 | if (!cptr->ump_info) { |
2204 | cptr->ump_info = kcalloc(NUM_UMP_INFOS, |
2205 | size: sizeof(void *), GFP_KERNEL); |
2206 | if (!cptr->ump_info) { |
2207 | err = -ENOMEM; |
2208 | goto error; |
2209 | } |
2210 | } |
2211 | p = memdup_user(argp->info, size); |
2212 | if (IS_ERR(ptr: p)) { |
2213 | err = PTR_ERR(ptr: p); |
2214 | goto error; |
2215 | } |
2216 | kfree(objp: cptr->ump_info[type]); |
2217 | terminate_ump_info_strings(p, type); |
2218 | cptr->ump_info[type] = p; |
2219 | } |
2220 | |
2221 | error: |
2222 | mutex_unlock(lock: &cptr->ioctl_mutex); |
2223 | snd_seq_client_unlock(cptr); |
2224 | return err; |
2225 | } |
2226 | #endif |
2227 | |
2228 | /* -------------------------------------------------------- */ |
2229 | |
2230 | static const struct ioctl_handler { |
2231 | unsigned int cmd; |
2232 | int (*func)(struct snd_seq_client *client, void *arg); |
2233 | } ioctl_handlers[] = { |
2234 | { SNDRV_SEQ_IOCTL_PVERSION, snd_seq_ioctl_pversion }, |
2235 | { SNDRV_SEQ_IOCTL_USER_PVERSION, snd_seq_ioctl_user_pversion }, |
2236 | { SNDRV_SEQ_IOCTL_CLIENT_ID, snd_seq_ioctl_client_id }, |
2237 | { SNDRV_SEQ_IOCTL_SYSTEM_INFO, snd_seq_ioctl_system_info }, |
2238 | { SNDRV_SEQ_IOCTL_RUNNING_MODE, snd_seq_ioctl_running_mode }, |
2239 | { SNDRV_SEQ_IOCTL_GET_CLIENT_INFO, snd_seq_ioctl_get_client_info }, |
2240 | { SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, snd_seq_ioctl_set_client_info }, |
2241 | { SNDRV_SEQ_IOCTL_CREATE_PORT, snd_seq_ioctl_create_port }, |
2242 | { SNDRV_SEQ_IOCTL_DELETE_PORT, snd_seq_ioctl_delete_port }, |
2243 | { SNDRV_SEQ_IOCTL_GET_PORT_INFO, snd_seq_ioctl_get_port_info }, |
2244 | { SNDRV_SEQ_IOCTL_SET_PORT_INFO, snd_seq_ioctl_set_port_info }, |
2245 | { SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, snd_seq_ioctl_subscribe_port }, |
2246 | { SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, snd_seq_ioctl_unsubscribe_port }, |
2247 | { SNDRV_SEQ_IOCTL_CREATE_QUEUE, snd_seq_ioctl_create_queue }, |
2248 | { SNDRV_SEQ_IOCTL_DELETE_QUEUE, snd_seq_ioctl_delete_queue }, |
2249 | { SNDRV_SEQ_IOCTL_GET_QUEUE_INFO, snd_seq_ioctl_get_queue_info }, |
2250 | { SNDRV_SEQ_IOCTL_SET_QUEUE_INFO, snd_seq_ioctl_set_queue_info }, |
2251 | { SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE, snd_seq_ioctl_get_named_queue }, |
2252 | { SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS, snd_seq_ioctl_get_queue_status }, |
2253 | { SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO, snd_seq_ioctl_get_queue_tempo }, |
2254 | { SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO, snd_seq_ioctl_set_queue_tempo }, |
2255 | { SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER, snd_seq_ioctl_get_queue_timer }, |
2256 | { SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER, snd_seq_ioctl_set_queue_timer }, |
2257 | { SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT, snd_seq_ioctl_get_queue_client }, |
2258 | { SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT, snd_seq_ioctl_set_queue_client }, |
2259 | { SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, snd_seq_ioctl_get_client_pool }, |
2260 | { SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, snd_seq_ioctl_set_client_pool }, |
2261 | { SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION, snd_seq_ioctl_get_subscription }, |
2262 | { SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, snd_seq_ioctl_query_next_client }, |
2263 | { SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, snd_seq_ioctl_query_next_port }, |
2264 | { SNDRV_SEQ_IOCTL_REMOVE_EVENTS, snd_seq_ioctl_remove_events }, |
2265 | { SNDRV_SEQ_IOCTL_QUERY_SUBS, snd_seq_ioctl_query_subs }, |
2266 | { 0, NULL }, |
2267 | }; |
2268 | |
2269 | static long snd_seq_ioctl(struct file *file, unsigned int cmd, |
2270 | unsigned long arg) |
2271 | { |
2272 | struct snd_seq_client *client = file->private_data; |
2273 | /* To use kernel stack for ioctl data. */ |
2274 | union { |
2275 | int pversion; |
2276 | int client_id; |
2277 | struct snd_seq_system_info system_info; |
2278 | struct snd_seq_running_info running_info; |
2279 | struct snd_seq_client_info client_info; |
2280 | struct snd_seq_port_info port_info; |
2281 | struct snd_seq_port_subscribe port_subscribe; |
2282 | struct snd_seq_queue_info queue_info; |
2283 | struct snd_seq_queue_status queue_status; |
2284 | struct snd_seq_queue_tempo tempo; |
2285 | struct snd_seq_queue_timer queue_timer; |
2286 | struct snd_seq_queue_client queue_client; |
2287 | struct snd_seq_client_pool client_pool; |
2288 | struct snd_seq_remove_events remove_events; |
2289 | struct snd_seq_query_subs query_subs; |
2290 | } buf; |
2291 | const struct ioctl_handler *handler; |
2292 | unsigned long size; |
2293 | int err; |
2294 | |
2295 | if (snd_BUG_ON(!client)) |
2296 | return -ENXIO; |
2297 | |
2298 | #if IS_ENABLED(CONFIG_SND_SEQ_UMP) |
2299 | /* exception - handling large data */ |
2300 | switch (cmd) { |
2301 | case SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO: |
2302 | case SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO: |
2303 | return snd_seq_ioctl_client_ump_info(caller: client, cmd, arg); |
2304 | } |
2305 | #endif |
2306 | |
2307 | for (handler = ioctl_handlers; handler->cmd > 0; ++handler) { |
2308 | if (handler->cmd == cmd) |
2309 | break; |
2310 | } |
2311 | if (handler->cmd == 0) |
2312 | return -ENOTTY; |
2313 | |
2314 | memset(&buf, 0, sizeof(buf)); |
2315 | |
2316 | /* |
2317 | * All of ioctl commands for ALSA sequencer get an argument of size |
2318 | * within 13 bits. We can safely pick up the size from the command. |
2319 | */ |
2320 | size = _IOC_SIZE(handler->cmd); |
2321 | if (handler->cmd & IOC_IN) { |
2322 | if (copy_from_user(to: &buf, from: (const void __user *)arg, n: size)) |
2323 | return -EFAULT; |
2324 | } |
2325 | |
2326 | mutex_lock(&client->ioctl_mutex); |
2327 | err = handler->func(client, &buf); |
2328 | mutex_unlock(lock: &client->ioctl_mutex); |
2329 | if (err >= 0) { |
2330 | /* Some commands includes a bug in 'dir' field. */ |
2331 | if (handler->cmd == SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT || |
2332 | handler->cmd == SNDRV_SEQ_IOCTL_SET_CLIENT_POOL || |
2333 | (handler->cmd & IOC_OUT)) |
2334 | if (copy_to_user(to: (void __user *)arg, from: &buf, n: size)) |
2335 | return -EFAULT; |
2336 | } |
2337 | |
2338 | return err; |
2339 | } |
2340 | |
2341 | #ifdef CONFIG_COMPAT |
2342 | #include "seq_compat.c" |
2343 | #else |
2344 | #define snd_seq_ioctl_compat NULL |
2345 | #endif |
2346 | |
2347 | /* -------------------------------------------------------- */ |
2348 | |
2349 | |
2350 | /* exported to kernel modules */ |
2351 | int snd_seq_create_kernel_client(struct snd_card *card, int client_index, |
2352 | const char *name_fmt, ...) |
2353 | { |
2354 | struct snd_seq_client *client; |
2355 | va_list args; |
2356 | |
2357 | if (snd_BUG_ON(in_interrupt())) |
2358 | return -EBUSY; |
2359 | |
2360 | if (card && client_index >= SNDRV_SEQ_CLIENTS_PER_CARD) |
2361 | return -EINVAL; |
2362 | if (card == NULL && client_index >= SNDRV_SEQ_GLOBAL_CLIENTS) |
2363 | return -EINVAL; |
2364 | |
2365 | mutex_lock(®ister_mutex); |
2366 | |
2367 | if (card) { |
2368 | client_index += SNDRV_SEQ_GLOBAL_CLIENTS |
2369 | + card->number * SNDRV_SEQ_CLIENTS_PER_CARD; |
2370 | if (client_index >= SNDRV_SEQ_DYNAMIC_CLIENTS_BEGIN) |
2371 | client_index = -1; |
2372 | } |
2373 | |
2374 | /* empty write queue as default */ |
2375 | client = seq_create_client1(client_index, poolsize: 0); |
2376 | if (client == NULL) { |
2377 | mutex_unlock(lock: ®ister_mutex); |
2378 | return -EBUSY; /* failure code */ |
2379 | } |
2380 | usage_alloc(res: &client_usage, num: 1); |
2381 | |
2382 | client->accept_input = 1; |
2383 | client->accept_output = 1; |
2384 | client->data.kernel.card = card; |
2385 | client->user_pversion = SNDRV_SEQ_VERSION; |
2386 | |
2387 | va_start(args, name_fmt); |
2388 | vsnprintf(buf: client->name, size: sizeof(client->name), fmt: name_fmt, args); |
2389 | va_end(args); |
2390 | |
2391 | client->type = KERNEL_CLIENT; |
2392 | mutex_unlock(lock: ®ister_mutex); |
2393 | |
2394 | /* make others aware this new client */ |
2395 | snd_seq_system_client_ev_client_start(client->number); |
2396 | |
2397 | /* return client number to caller */ |
2398 | return client->number; |
2399 | } |
2400 | EXPORT_SYMBOL(snd_seq_create_kernel_client); |
2401 | |
2402 | /* exported to kernel modules */ |
2403 | int snd_seq_delete_kernel_client(int client) |
2404 | { |
2405 | struct snd_seq_client *ptr; |
2406 | |
2407 | if (snd_BUG_ON(in_interrupt())) |
2408 | return -EBUSY; |
2409 | |
2410 | ptr = clientptr(clientid: client); |
2411 | if (ptr == NULL) |
2412 | return -EINVAL; |
2413 | |
2414 | seq_free_client(client: ptr); |
2415 | kfree(objp: ptr); |
2416 | return 0; |
2417 | } |
2418 | EXPORT_SYMBOL(snd_seq_delete_kernel_client); |
2419 | |
2420 | /* |
2421 | * exported, called by kernel clients to enqueue events (w/o blocking) |
2422 | * |
2423 | * RETURN VALUE: zero if succeed, negative if error |
2424 | */ |
2425 | int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event *ev, |
2426 | struct file *file, bool blocking) |
2427 | { |
2428 | struct snd_seq_client *cptr; |
2429 | int result; |
2430 | |
2431 | if (snd_BUG_ON(!ev)) |
2432 | return -EINVAL; |
2433 | |
2434 | if (!snd_seq_ev_is_ump(ev)) { |
2435 | if (ev->type == SNDRV_SEQ_EVENT_NONE) |
2436 | return 0; /* ignore this */ |
2437 | if (ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR) |
2438 | return -EINVAL; /* quoted events can't be enqueued */ |
2439 | } |
2440 | |
2441 | /* fill in client number */ |
2442 | ev->source.client = client; |
2443 | |
2444 | if (check_event_type_and_length(ev)) |
2445 | return -EINVAL; |
2446 | |
2447 | cptr = snd_seq_client_use_ptr(clientid: client); |
2448 | if (cptr == NULL) |
2449 | return -EINVAL; |
2450 | |
2451 | if (!cptr->accept_output) { |
2452 | result = -EPERM; |
2453 | } else { /* send it */ |
2454 | mutex_lock(&cptr->ioctl_mutex); |
2455 | result = snd_seq_client_enqueue_event(client: cptr, event: ev, file, blocking, |
2456 | atomic: false, hop: 0, |
2457 | mutexp: &cptr->ioctl_mutex); |
2458 | mutex_unlock(lock: &cptr->ioctl_mutex); |
2459 | } |
2460 | |
2461 | snd_seq_client_unlock(cptr); |
2462 | return result; |
2463 | } |
2464 | EXPORT_SYMBOL(snd_seq_kernel_client_enqueue); |
2465 | |
2466 | /* |
2467 | * exported, called by kernel clients to dispatch events directly to other |
2468 | * clients, bypassing the queues. Event time-stamp will be updated. |
2469 | * |
2470 | * RETURN VALUE: negative = delivery failed, |
2471 | * zero, or positive: the number of delivered events |
2472 | */ |
2473 | int snd_seq_kernel_client_dispatch(int client, struct snd_seq_event * ev, |
2474 | int atomic, int hop) |
2475 | { |
2476 | struct snd_seq_client *cptr; |
2477 | int result; |
2478 | |
2479 | if (snd_BUG_ON(!ev)) |
2480 | return -EINVAL; |
2481 | |
2482 | /* fill in client number */ |
2483 | ev->queue = SNDRV_SEQ_QUEUE_DIRECT; |
2484 | ev->source.client = client; |
2485 | |
2486 | if (check_event_type_and_length(ev)) |
2487 | return -EINVAL; |
2488 | |
2489 | cptr = snd_seq_client_use_ptr(clientid: client); |
2490 | if (cptr == NULL) |
2491 | return -EINVAL; |
2492 | |
2493 | if (!cptr->accept_output) |
2494 | result = -EPERM; |
2495 | else |
2496 | result = snd_seq_deliver_event(client: cptr, event: ev, atomic, hop); |
2497 | |
2498 | snd_seq_client_unlock(cptr); |
2499 | return result; |
2500 | } |
2501 | EXPORT_SYMBOL(snd_seq_kernel_client_dispatch); |
2502 | |
2503 | /** |
2504 | * snd_seq_kernel_client_ctl - operate a command for a client with data in |
2505 | * kernel space. |
2506 | * @clientid: A numerical ID for a client. |
2507 | * @cmd: An ioctl(2) command for ALSA sequencer operation. |
2508 | * @arg: A pointer to data in kernel space. |
2509 | * |
2510 | * Against its name, both kernel/application client can be handled by this |
2511 | * kernel API. A pointer of 'arg' argument should be in kernel space. |
2512 | * |
2513 | * Return: 0 at success. Negative error code at failure. |
2514 | */ |
2515 | int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg) |
2516 | { |
2517 | const struct ioctl_handler *handler; |
2518 | struct snd_seq_client *client; |
2519 | |
2520 | client = clientptr(clientid); |
2521 | if (client == NULL) |
2522 | return -ENXIO; |
2523 | |
2524 | for (handler = ioctl_handlers; handler->cmd > 0; ++handler) { |
2525 | if (handler->cmd == cmd) |
2526 | return handler->func(client, arg); |
2527 | } |
2528 | |
2529 | pr_debug("ALSA: seq unknown ioctl() 0x%x (type='%c', number=0x%02x)\n" , |
2530 | cmd, _IOC_TYPE(cmd), _IOC_NR(cmd)); |
2531 | return -ENOTTY; |
2532 | } |
2533 | EXPORT_SYMBOL(snd_seq_kernel_client_ctl); |
2534 | |
2535 | /* exported (for OSS emulator) */ |
2536 | int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait) |
2537 | { |
2538 | struct snd_seq_client *client; |
2539 | |
2540 | client = clientptr(clientid); |
2541 | if (client == NULL) |
2542 | return -ENXIO; |
2543 | |
2544 | if (! snd_seq_write_pool_allocated(client)) |
2545 | return 1; |
2546 | if (snd_seq_pool_poll_wait(pool: client->pool, file, wait)) |
2547 | return 1; |
2548 | return 0; |
2549 | } |
2550 | EXPORT_SYMBOL(snd_seq_kernel_client_write_poll); |
2551 | |
2552 | /* get a sequencer client object; for internal use from a kernel client */ |
2553 | struct snd_seq_client *snd_seq_kernel_client_get(int id) |
2554 | { |
2555 | return snd_seq_client_use_ptr(clientid: id); |
2556 | } |
2557 | EXPORT_SYMBOL_GPL(snd_seq_kernel_client_get); |
2558 | |
2559 | /* put a sequencer client object; for internal use from a kernel client */ |
2560 | void snd_seq_kernel_client_put(struct snd_seq_client *cptr) |
2561 | { |
2562 | if (cptr) |
2563 | snd_seq_client_unlock(cptr); |
2564 | } |
2565 | EXPORT_SYMBOL_GPL(snd_seq_kernel_client_put); |
2566 | |
2567 | /*---------------------------------------------------------------------------*/ |
2568 | |
2569 | #ifdef CONFIG_SND_PROC_FS |
2570 | /* |
2571 | * /proc interface |
2572 | */ |
2573 | static void snd_seq_info_dump_subscribers(struct snd_info_buffer *buffer, |
2574 | struct snd_seq_port_subs_info *group, |
2575 | int is_src, char *msg) |
2576 | { |
2577 | struct list_head *p; |
2578 | struct snd_seq_subscribers *s; |
2579 | int count = 0; |
2580 | |
2581 | down_read(sem: &group->list_mutex); |
2582 | if (list_empty(head: &group->list_head)) { |
2583 | up_read(sem: &group->list_mutex); |
2584 | return; |
2585 | } |
2586 | snd_iprintf(buffer, msg); |
2587 | list_for_each(p, &group->list_head) { |
2588 | if (is_src) |
2589 | s = list_entry(p, struct snd_seq_subscribers, src_list); |
2590 | else |
2591 | s = list_entry(p, struct snd_seq_subscribers, dest_list); |
2592 | if (count++) |
2593 | snd_iprintf(buffer, ", " ); |
2594 | snd_iprintf(buffer, "%d:%d" , |
2595 | is_src ? s->info.dest.client : s->info.sender.client, |
2596 | is_src ? s->info.dest.port : s->info.sender.port); |
2597 | if (s->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP) |
2598 | snd_iprintf(buffer, "[%c:%d]" , ((s->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL) ? 'r' : 't'), s->info.queue); |
2599 | if (group->exclusive) |
2600 | snd_iprintf(buffer, "[ex]" ); |
2601 | } |
2602 | up_read(sem: &group->list_mutex); |
2603 | snd_iprintf(buffer, "\n" ); |
2604 | } |
2605 | |
2606 | #define FLAG_PERM_RD(perm) ((perm) & SNDRV_SEQ_PORT_CAP_READ ? ((perm) & SNDRV_SEQ_PORT_CAP_SUBS_READ ? 'R' : 'r') : '-') |
2607 | #define FLAG_PERM_WR(perm) ((perm) & SNDRV_SEQ_PORT_CAP_WRITE ? ((perm) & SNDRV_SEQ_PORT_CAP_SUBS_WRITE ? 'W' : 'w') : '-') |
2608 | #define FLAG_PERM_EX(perm) ((perm) & SNDRV_SEQ_PORT_CAP_NO_EXPORT ? '-' : 'e') |
2609 | |
2610 | #define FLAG_PERM_DUPLEX(perm) ((perm) & SNDRV_SEQ_PORT_CAP_DUPLEX ? 'X' : '-') |
2611 | |
2612 | static const char *port_direction_name(unsigned char dir) |
2613 | { |
2614 | static const char *names[4] = { |
2615 | "-" , "In" , "Out" , "In/Out" |
2616 | }; |
2617 | |
2618 | if (dir > SNDRV_SEQ_PORT_DIR_BIDIRECTION) |
2619 | return "Invalid" ; |
2620 | return names[dir]; |
2621 | } |
2622 | |
2623 | static void snd_seq_info_dump_ports(struct snd_info_buffer *buffer, |
2624 | struct snd_seq_client *client) |
2625 | { |
2626 | struct snd_seq_client_port *p; |
2627 | |
2628 | mutex_lock(&client->ports_mutex); |
2629 | list_for_each_entry(p, &client->ports_list_head, list) { |
2630 | if (p->capability & SNDRV_SEQ_PORT_CAP_INACTIVE) |
2631 | continue; |
2632 | snd_iprintf(buffer, " Port %3d : \"%s\" (%c%c%c%c) [%s]\n" , |
2633 | p->addr.port, p->name, |
2634 | FLAG_PERM_RD(p->capability), |
2635 | FLAG_PERM_WR(p->capability), |
2636 | FLAG_PERM_EX(p->capability), |
2637 | FLAG_PERM_DUPLEX(p->capability), |
2638 | port_direction_name(p->direction)); |
2639 | snd_seq_info_dump_subscribers(buffer, group: &p->c_src, is_src: 1, msg: " Connecting To: " ); |
2640 | snd_seq_info_dump_subscribers(buffer, group: &p->c_dest, is_src: 0, msg: " Connected From: " ); |
2641 | } |
2642 | mutex_unlock(lock: &client->ports_mutex); |
2643 | } |
2644 | |
2645 | static const char *midi_version_string(unsigned int version) |
2646 | { |
2647 | switch (version) { |
2648 | case SNDRV_SEQ_CLIENT_LEGACY_MIDI: |
2649 | return "Legacy" ; |
2650 | case SNDRV_SEQ_CLIENT_UMP_MIDI_1_0: |
2651 | return "UMP MIDI1" ; |
2652 | case SNDRV_SEQ_CLIENT_UMP_MIDI_2_0: |
2653 | return "UMP MIDI2" ; |
2654 | default: |
2655 | return "Unknown" ; |
2656 | } |
2657 | } |
2658 | |
2659 | /* exported to seq_info.c */ |
2660 | void snd_seq_info_clients_read(struct snd_info_entry *entry, |
2661 | struct snd_info_buffer *buffer) |
2662 | { |
2663 | int c; |
2664 | struct snd_seq_client *client; |
2665 | |
2666 | snd_iprintf(buffer, "Client info\n" ); |
2667 | snd_iprintf(buffer, " cur clients : %d\n" , client_usage.cur); |
2668 | snd_iprintf(buffer, " peak clients : %d\n" , client_usage.peak); |
2669 | snd_iprintf(buffer, " max clients : %d\n" , SNDRV_SEQ_MAX_CLIENTS); |
2670 | snd_iprintf(buffer, "\n" ); |
2671 | |
2672 | /* list the client table */ |
2673 | for (c = 0; c < SNDRV_SEQ_MAX_CLIENTS; c++) { |
2674 | client = snd_seq_client_use_ptr(clientid: c); |
2675 | if (client == NULL) |
2676 | continue; |
2677 | if (client->type == NO_CLIENT) { |
2678 | snd_seq_client_unlock(client); |
2679 | continue; |
2680 | } |
2681 | |
2682 | snd_iprintf(buffer, "Client %3d : \"%s\" [%s %s]\n" , |
2683 | c, client->name, |
2684 | client->type == USER_CLIENT ? "User" : "Kernel" , |
2685 | midi_version_string(client->midi_version)); |
2686 | #if IS_ENABLED(CONFIG_SND_SEQ_UMP) |
2687 | dump_ump_info(buffer, client); |
2688 | #endif |
2689 | snd_seq_info_dump_ports(buffer, client); |
2690 | if (snd_seq_write_pool_allocated(client)) { |
2691 | snd_iprintf(buffer, " Output pool :\n" ); |
2692 | snd_seq_info_pool(buffer, pool: client->pool, space: " " ); |
2693 | } |
2694 | if (client->type == USER_CLIENT && client->data.user.fifo && |
2695 | client->data.user.fifo->pool) { |
2696 | snd_iprintf(buffer, " Input pool :\n" ); |
2697 | snd_seq_info_pool(buffer, pool: client->data.user.fifo->pool, space: " " ); |
2698 | } |
2699 | snd_seq_client_unlock(client); |
2700 | } |
2701 | } |
2702 | #endif /* CONFIG_SND_PROC_FS */ |
2703 | |
2704 | /*---------------------------------------------------------------------------*/ |
2705 | |
2706 | |
2707 | /* |
2708 | * REGISTRATION PART |
2709 | */ |
2710 | |
2711 | static const struct file_operations snd_seq_f_ops = |
2712 | { |
2713 | .owner = THIS_MODULE, |
2714 | .read = snd_seq_read, |
2715 | .write = snd_seq_write, |
2716 | .open = snd_seq_open, |
2717 | .release = snd_seq_release, |
2718 | .llseek = no_llseek, |
2719 | .poll = snd_seq_poll, |
2720 | .unlocked_ioctl = snd_seq_ioctl, |
2721 | .compat_ioctl = snd_seq_ioctl_compat, |
2722 | }; |
2723 | |
2724 | static struct device *seq_dev; |
2725 | |
2726 | /* |
2727 | * register sequencer device |
2728 | */ |
2729 | int __init snd_sequencer_device_init(void) |
2730 | { |
2731 | int err; |
2732 | |
2733 | err = snd_device_alloc(dev_p: &seq_dev, NULL); |
2734 | if (err < 0) |
2735 | return err; |
2736 | dev_set_name(dev: seq_dev, name: "seq" ); |
2737 | |
2738 | mutex_lock(®ister_mutex); |
2739 | err = snd_register_device(type: SNDRV_DEVICE_TYPE_SEQUENCER, NULL, dev: 0, |
2740 | f_ops: &snd_seq_f_ops, NULL, device: seq_dev); |
2741 | mutex_unlock(lock: ®ister_mutex); |
2742 | if (err < 0) { |
2743 | put_device(dev: seq_dev); |
2744 | return err; |
2745 | } |
2746 | |
2747 | return 0; |
2748 | } |
2749 | |
2750 | |
2751 | |
2752 | /* |
2753 | * unregister sequencer device |
2754 | */ |
2755 | void snd_sequencer_device_done(void) |
2756 | { |
2757 | snd_unregister_device(dev: seq_dev); |
2758 | put_device(dev: seq_dev); |
2759 | } |
2760 | |