11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * ALSA sequencer Client Manager
41da177e4SLinus Torvalds * Copyright (c) 1998-2001 by Frank van de Pol <fvdpol@coil.demon.nl>
5c1017a4cSJaroslav Kysela * Jaroslav Kysela <perex@perex.cz>
61da177e4SLinus Torvalds * Takashi Iwai <tiwai@suse.de>
71da177e4SLinus Torvalds */
81da177e4SLinus Torvalds
91da177e4SLinus Torvalds #include <linux/init.h>
10d81a6d71SPaul Gortmaker #include <linux/export.h>
111da177e4SLinus Torvalds #include <linux/slab.h>
121da177e4SLinus Torvalds #include <sound/core.h>
131da177e4SLinus Torvalds #include <sound/minors.h>
141da177e4SLinus Torvalds #include <linux/kmod.h>
151da177e4SLinus Torvalds
161da177e4SLinus Torvalds #include <sound/seq_kernel.h>
17d2d247e3STakashi Iwai #include <sound/ump.h>
181da177e4SLinus Torvalds #include "seq_clientmgr.h"
191da177e4SLinus Torvalds #include "seq_memory.h"
201da177e4SLinus Torvalds #include "seq_queue.h"
211da177e4SLinus Torvalds #include "seq_timer.h"
221da177e4SLinus Torvalds #include "seq_info.h"
231da177e4SLinus Torvalds #include "seq_system.h"
24e9e02819STakashi Iwai #include "seq_ump_convert.h"
251da177e4SLinus Torvalds #include <sound/seq_device.h>
261da177e4SLinus Torvalds #ifdef CONFIG_COMPAT
271da177e4SLinus Torvalds #include <linux/compat.h>
281da177e4SLinus Torvalds #endif
291da177e4SLinus Torvalds
301da177e4SLinus Torvalds /* Client Manager
311da177e4SLinus Torvalds
321da177e4SLinus Torvalds * this module handles the connections of userland and kernel clients
331da177e4SLinus Torvalds *
341da177e4SLinus Torvalds */
351da177e4SLinus Torvalds
36aa1e77e6SClemens Ladisch /*
37aa1e77e6SClemens Ladisch * There are four ranges of client numbers (last two shared):
38aa1e77e6SClemens Ladisch * 0..15: global clients
39aa1e77e6SClemens Ladisch * 16..127: statically allocated client numbers for cards 0..27
40aa1e77e6SClemens Ladisch * 128..191: dynamically allocated client numbers for cards 28..31
41aa1e77e6SClemens Ladisch * 128..191: dynamically allocated client numbers for applications
42aa1e77e6SClemens Ladisch */
43aa1e77e6SClemens Ladisch
44aa1e77e6SClemens Ladisch /* number of kernel non-card clients */
45aa1e77e6SClemens Ladisch #define SNDRV_SEQ_GLOBAL_CLIENTS 16
46aa1e77e6SClemens Ladisch /* clients per cards, for static clients */
47aa1e77e6SClemens Ladisch #define SNDRV_SEQ_CLIENTS_PER_CARD 4
48aa1e77e6SClemens Ladisch /* dynamically allocated client numbers (both kernel drivers and user space) */
49aa1e77e6SClemens Ladisch #define SNDRV_SEQ_DYNAMIC_CLIENTS_BEGIN 128
50d001544dSClemens Ladisch
511da177e4SLinus Torvalds #define SNDRV_SEQ_LFLG_INPUT 0x0001
521da177e4SLinus Torvalds #define SNDRV_SEQ_LFLG_OUTPUT 0x0002
531da177e4SLinus Torvalds #define SNDRV_SEQ_LFLG_OPEN (SNDRV_SEQ_LFLG_INPUT|SNDRV_SEQ_LFLG_OUTPUT)
541da177e4SLinus Torvalds
551da177e4SLinus Torvalds static DEFINE_SPINLOCK(clients_lock);
561a60d4c5SIngo Molnar static DEFINE_MUTEX(register_mutex);
571da177e4SLinus Torvalds
581da177e4SLinus Torvalds /*
591da177e4SLinus Torvalds * client table
601da177e4SLinus Torvalds */
611da177e4SLinus Torvalds static char clienttablock[SNDRV_SEQ_MAX_CLIENTS];
62c7e0b5bfSTakashi Iwai static struct snd_seq_client *clienttab[SNDRV_SEQ_MAX_CLIENTS];
63c7e0b5bfSTakashi Iwai static struct snd_seq_usage client_usage;
641da177e4SLinus Torvalds
651da177e4SLinus Torvalds /*
661da177e4SLinus Torvalds * prototypes
671da177e4SLinus Torvalds */
68c7e0b5bfSTakashi Iwai static int bounce_error_event(struct snd_seq_client *client,
69c7e0b5bfSTakashi Iwai struct snd_seq_event *event,
70c7e0b5bfSTakashi Iwai int err, int atomic, int hop);
71c7e0b5bfSTakashi Iwai static int snd_seq_deliver_single_event(struct snd_seq_client *client,
72c7e0b5bfSTakashi Iwai struct snd_seq_event *event,
73c7e0b5bfSTakashi Iwai int filter, int atomic, int hop);
741da177e4SLinus Torvalds
75d2d247e3STakashi Iwai #if IS_ENABLED(CONFIG_SND_SEQ_UMP)
76d2d247e3STakashi Iwai static void free_ump_info(struct snd_seq_client *client);
77d2d247e3STakashi Iwai #endif
78d2d247e3STakashi Iwai
791da177e4SLinus Torvalds /*
801da177e4SLinus Torvalds */
snd_seq_file_flags(struct file * file)811da177e4SLinus Torvalds static inline unsigned short snd_seq_file_flags(struct file *file)
821da177e4SLinus Torvalds {
831da177e4SLinus Torvalds switch (file->f_mode & (FMODE_READ | FMODE_WRITE)) {
841da177e4SLinus Torvalds case FMODE_WRITE:
851da177e4SLinus Torvalds return SNDRV_SEQ_LFLG_OUTPUT;
861da177e4SLinus Torvalds case FMODE_READ:
871da177e4SLinus Torvalds return SNDRV_SEQ_LFLG_INPUT;
881da177e4SLinus Torvalds default:
891da177e4SLinus Torvalds return SNDRV_SEQ_LFLG_OPEN;
901da177e4SLinus Torvalds }
911da177e4SLinus Torvalds }
921da177e4SLinus Torvalds
snd_seq_write_pool_allocated(struct snd_seq_client * client)93c7e0b5bfSTakashi Iwai static inline int snd_seq_write_pool_allocated(struct snd_seq_client *client)
941da177e4SLinus Torvalds {
951da177e4SLinus Torvalds return snd_seq_total_cells(client->pool) > 0;
961da177e4SLinus Torvalds }
971da177e4SLinus Torvalds
981da177e4SLinus Torvalds /* return pointer to client structure for specified id */
clientptr(int clientid)99c7e0b5bfSTakashi Iwai static struct snd_seq_client *clientptr(int clientid)
1001da177e4SLinus Torvalds {
1011da177e4SLinus Torvalds if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) {
10204cc79a0STakashi Iwai pr_debug("ALSA: seq: oops. Trying to get pointer to client %d\n",
103c7e0b5bfSTakashi Iwai clientid);
1041da177e4SLinus Torvalds return NULL;
1051da177e4SLinus Torvalds }
1061da177e4SLinus Torvalds return clienttab[clientid];
1071da177e4SLinus Torvalds }
1081da177e4SLinus Torvalds
client_use_ptr(int clientid,bool load_module)109*5e1b3bf7STakashi Iwai static struct snd_seq_client *client_use_ptr(int clientid, bool load_module)
1101da177e4SLinus Torvalds {
1111da177e4SLinus Torvalds unsigned long flags;
112c7e0b5bfSTakashi Iwai struct snd_seq_client *client;
1131da177e4SLinus Torvalds
1141da177e4SLinus Torvalds if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) {
11504cc79a0STakashi Iwai pr_debug("ALSA: seq: oops. Trying to get pointer to client %d\n",
116c7e0b5bfSTakashi Iwai clientid);
1171da177e4SLinus Torvalds return NULL;
1181da177e4SLinus Torvalds }
1191da177e4SLinus Torvalds spin_lock_irqsave(&clients_lock, flags);
1201da177e4SLinus Torvalds client = clientptr(clientid);
1211da177e4SLinus Torvalds if (client)
1221da177e4SLinus Torvalds goto __lock;
1231da177e4SLinus Torvalds if (clienttablock[clientid]) {
1241da177e4SLinus Torvalds spin_unlock_irqrestore(&clients_lock, flags);
1251da177e4SLinus Torvalds return NULL;
1261da177e4SLinus Torvalds }
1271da177e4SLinus Torvalds spin_unlock_irqrestore(&clients_lock, flags);
128ee2da997SJohannes Berg #ifdef CONFIG_MODULES
129*5e1b3bf7STakashi Iwai if (load_module) {
1303e7e04b7STakashi Iwai static DECLARE_BITMAP(client_requested, SNDRV_SEQ_GLOBAL_CLIENTS);
1313e7e04b7STakashi Iwai static DECLARE_BITMAP(card_requested, SNDRV_CARDS);
1323e7e04b7STakashi Iwai
133aa1e77e6SClemens Ladisch if (clientid < SNDRV_SEQ_GLOBAL_CLIENTS) {
1341da177e4SLinus Torvalds int idx;
1351da177e4SLinus Torvalds
1363e7e04b7STakashi Iwai if (!test_and_set_bit(clientid, client_requested)) {
137aa1e77e6SClemens Ladisch for (idx = 0; idx < 15; idx++) {
1381da177e4SLinus Torvalds if (seq_client_load[idx] < 0)
1391da177e4SLinus Torvalds break;
1401da177e4SLinus Torvalds if (seq_client_load[idx] == clientid) {
141c7e0b5bfSTakashi Iwai request_module("snd-seq-client-%i",
142c7e0b5bfSTakashi Iwai clientid);
1431da177e4SLinus Torvalds break;
1441da177e4SLinus Torvalds }
1451da177e4SLinus Torvalds }
1461da177e4SLinus Torvalds }
147aa1e77e6SClemens Ladisch } else if (clientid < SNDRV_SEQ_DYNAMIC_CLIENTS_BEGIN) {
148aa1e77e6SClemens Ladisch int card = (clientid - SNDRV_SEQ_GLOBAL_CLIENTS) /
149aa1e77e6SClemens Ladisch SNDRV_SEQ_CLIENTS_PER_CARD;
1501da177e4SLinus Torvalds if (card < snd_ecards_limit) {
1513e7e04b7STakashi Iwai if (!test_and_set_bit(card, card_requested))
1521da177e4SLinus Torvalds snd_request_card(card);
1531da177e4SLinus Torvalds snd_seq_device_load_drivers();
1541da177e4SLinus Torvalds }
1551da177e4SLinus Torvalds }
1561da177e4SLinus Torvalds spin_lock_irqsave(&clients_lock, flags);
1571da177e4SLinus Torvalds client = clientptr(clientid);
1581da177e4SLinus Torvalds if (client)
1591da177e4SLinus Torvalds goto __lock;
1601da177e4SLinus Torvalds spin_unlock_irqrestore(&clients_lock, flags);
1611da177e4SLinus Torvalds }
1621da177e4SLinus Torvalds #endif
1631da177e4SLinus Torvalds return NULL;
1641da177e4SLinus Torvalds
1651da177e4SLinus Torvalds __lock:
1661da177e4SLinus Torvalds snd_use_lock_use(&client->use_lock);
1671da177e4SLinus Torvalds spin_unlock_irqrestore(&clients_lock, flags);
1681da177e4SLinus Torvalds return client;
1691da177e4SLinus Torvalds }
1701da177e4SLinus Torvalds
171*5e1b3bf7STakashi Iwai /* get snd_seq_client object for the given id quickly */
snd_seq_client_use_ptr(int clientid)172*5e1b3bf7STakashi Iwai struct snd_seq_client *snd_seq_client_use_ptr(int clientid)
173*5e1b3bf7STakashi Iwai {
174*5e1b3bf7STakashi Iwai return client_use_ptr(clientid, false);
175*5e1b3bf7STakashi Iwai }
176*5e1b3bf7STakashi Iwai
177*5e1b3bf7STakashi Iwai /* get snd_seq_client object for the given id;
178*5e1b3bf7STakashi Iwai * if not found, retry after loading the modules
179*5e1b3bf7STakashi Iwai */
client_load_and_use_ptr(int clientid)180*5e1b3bf7STakashi Iwai static struct snd_seq_client *client_load_and_use_ptr(int clientid)
181*5e1b3bf7STakashi Iwai {
182*5e1b3bf7STakashi Iwai return client_use_ptr(clientid, IS_ENABLED(CONFIG_MODULES));
183*5e1b3bf7STakashi Iwai }
184*5e1b3bf7STakashi Iwai
1856b580f52STakashi Iwai /* Take refcount and perform ioctl_mutex lock on the given client;
1866b580f52STakashi Iwai * used only for OSS sequencer
1876b580f52STakashi Iwai * Unlock via snd_seq_client_ioctl_unlock() below
1886b580f52STakashi Iwai */
snd_seq_client_ioctl_lock(int clientid)1896b580f52STakashi Iwai bool snd_seq_client_ioctl_lock(int clientid)
1906b580f52STakashi Iwai {
1916b580f52STakashi Iwai struct snd_seq_client *client;
1926b580f52STakashi Iwai
193*5e1b3bf7STakashi Iwai client = client_load_and_use_ptr(clientid);
1946b580f52STakashi Iwai if (!client)
1956b580f52STakashi Iwai return false;
1966b580f52STakashi Iwai mutex_lock(&client->ioctl_mutex);
197b5fd12d6STakashi Iwai /* The client isn't unrefed here; see snd_seq_client_ioctl_unlock() */
1986b580f52STakashi Iwai return true;
1996b580f52STakashi Iwai }
2006b580f52STakashi Iwai EXPORT_SYMBOL_GPL(snd_seq_client_ioctl_lock);
2016b580f52STakashi Iwai
2026b580f52STakashi Iwai /* Unlock and unref the given client; for OSS sequencer use only */
snd_seq_client_ioctl_unlock(int clientid)2036b580f52STakashi Iwai void snd_seq_client_ioctl_unlock(int clientid)
2046b580f52STakashi Iwai {
2056b580f52STakashi Iwai struct snd_seq_client *client;
2066b580f52STakashi Iwai
2076b580f52STakashi Iwai client = snd_seq_client_use_ptr(clientid);
2086b580f52STakashi Iwai if (WARN_ON(!client))
2096b580f52STakashi Iwai return;
2106b580f52STakashi Iwai mutex_unlock(&client->ioctl_mutex);
211b5fd12d6STakashi Iwai /* The doubly unrefs below are intentional; the first one releases the
212b5fd12d6STakashi Iwai * leftover from snd_seq_client_ioctl_lock() above, and the second one
213b5fd12d6STakashi Iwai * is for releasing snd_seq_client_use_ptr() in this function
214b5fd12d6STakashi Iwai */
215b5fd12d6STakashi Iwai snd_seq_client_unlock(client);
2166b580f52STakashi Iwai snd_seq_client_unlock(client);
2176b580f52STakashi Iwai }
2186b580f52STakashi Iwai EXPORT_SYMBOL_GPL(snd_seq_client_ioctl_unlock);
2196b580f52STakashi Iwai
usage_alloc(struct snd_seq_usage * res,int num)220c7e0b5bfSTakashi Iwai static void usage_alloc(struct snd_seq_usage *res, int num)
2211da177e4SLinus Torvalds {
2221da177e4SLinus Torvalds res->cur += num;
2231da177e4SLinus Torvalds if (res->cur > res->peak)
2241da177e4SLinus Torvalds res->peak = res->cur;
2251da177e4SLinus Torvalds }
2261da177e4SLinus Torvalds
usage_free(struct snd_seq_usage * res,int num)227c7e0b5bfSTakashi Iwai static void usage_free(struct snd_seq_usage *res, int num)
2281da177e4SLinus Torvalds {
2291da177e4SLinus Torvalds res->cur -= num;
2301da177e4SLinus Torvalds }
2311da177e4SLinus Torvalds
2321da177e4SLinus Torvalds /* initialise data structures */
client_init_data(void)2331da177e4SLinus Torvalds int __init client_init_data(void)
2341da177e4SLinus Torvalds {
2351da177e4SLinus Torvalds /* zap out the client table */
2361da177e4SLinus Torvalds memset(&clienttablock, 0, sizeof(clienttablock));
2371da177e4SLinus Torvalds memset(&clienttab, 0, sizeof(clienttab));
2381da177e4SLinus Torvalds return 0;
2391da177e4SLinus Torvalds }
2401da177e4SLinus Torvalds
2411da177e4SLinus Torvalds
seq_create_client1(int client_index,int poolsize)242aa1e77e6SClemens Ladisch static struct snd_seq_client *seq_create_client1(int client_index, int poolsize)
2431da177e4SLinus Torvalds {
2441da177e4SLinus Torvalds int c;
245c7e0b5bfSTakashi Iwai struct snd_seq_client *client;
2461da177e4SLinus Torvalds
2471da177e4SLinus Torvalds /* init client data */
248ecca82b4STakashi Iwai client = kzalloc(sizeof(*client), GFP_KERNEL);
2491da177e4SLinus Torvalds if (client == NULL)
2501da177e4SLinus Torvalds return NULL;
2511da177e4SLinus Torvalds client->pool = snd_seq_pool_new(poolsize);
2521da177e4SLinus Torvalds if (client->pool == NULL) {
2531da177e4SLinus Torvalds kfree(client);
2541da177e4SLinus Torvalds return NULL;
2551da177e4SLinus Torvalds }
2561da177e4SLinus Torvalds client->type = NO_CLIENT;
2571da177e4SLinus Torvalds snd_use_lock_init(&client->use_lock);
2581da177e4SLinus Torvalds rwlock_init(&client->ports_lock);
2591a60d4c5SIngo Molnar mutex_init(&client->ports_mutex);
2601da177e4SLinus Torvalds INIT_LIST_HEAD(&client->ports_list_head);
261b3defb79STakashi Iwai mutex_init(&client->ioctl_mutex);
262177ccf81STakashi Iwai client->ump_endpoint_port = -1;
2631da177e4SLinus Torvalds
2641da177e4SLinus Torvalds /* find free slot in the client table */
265f823b8a7STakashi Iwai spin_lock_irq(&clients_lock);
2661da177e4SLinus Torvalds if (client_index < 0) {
267aa1e77e6SClemens Ladisch for (c = SNDRV_SEQ_DYNAMIC_CLIENTS_BEGIN;
268aa1e77e6SClemens Ladisch c < SNDRV_SEQ_MAX_CLIENTS;
269aa1e77e6SClemens Ladisch c++) {
2701da177e4SLinus Torvalds if (clienttab[c] || clienttablock[c])
2711da177e4SLinus Torvalds continue;
2721da177e4SLinus Torvalds clienttab[client->number = c] = client;
273f823b8a7STakashi Iwai spin_unlock_irq(&clients_lock);
2741da177e4SLinus Torvalds return client;
2751da177e4SLinus Torvalds }
2761da177e4SLinus Torvalds } else {
2771da177e4SLinus Torvalds if (clienttab[client_index] == NULL && !clienttablock[client_index]) {
2781da177e4SLinus Torvalds clienttab[client->number = client_index] = client;
279f823b8a7STakashi Iwai spin_unlock_irq(&clients_lock);
2801da177e4SLinus Torvalds return client;
2811da177e4SLinus Torvalds }
2821da177e4SLinus Torvalds }
283f823b8a7STakashi Iwai spin_unlock_irq(&clients_lock);
2841da177e4SLinus Torvalds snd_seq_pool_delete(&client->pool);
2851da177e4SLinus Torvalds kfree(client);
2861da177e4SLinus Torvalds return NULL; /* no free slot found or busy, return failure code */
2871da177e4SLinus Torvalds }
2881da177e4SLinus Torvalds
2891da177e4SLinus Torvalds
seq_free_client1(struct snd_seq_client * client)290c7e0b5bfSTakashi Iwai static int seq_free_client1(struct snd_seq_client *client)
2911da177e4SLinus Torvalds {
2927eaa943cSTakashi Iwai if (!client)
2937eaa943cSTakashi Iwai return 0;
294f823b8a7STakashi Iwai spin_lock_irq(&clients_lock);
2951da177e4SLinus Torvalds clienttablock[client->number] = 1;
2961da177e4SLinus Torvalds clienttab[client->number] = NULL;
297f823b8a7STakashi Iwai spin_unlock_irq(&clients_lock);
298a2ff19f7STakashi Iwai snd_seq_delete_all_ports(client);
299a2ff19f7STakashi Iwai snd_seq_queue_client_leave(client->number);
3001da177e4SLinus Torvalds snd_use_lock_sync(&client->use_lock);
3011da177e4SLinus Torvalds if (client->pool)
3021da177e4SLinus Torvalds snd_seq_pool_delete(&client->pool);
303f823b8a7STakashi Iwai spin_lock_irq(&clients_lock);
3041da177e4SLinus Torvalds clienttablock[client->number] = 0;
305f823b8a7STakashi Iwai spin_unlock_irq(&clients_lock);
3061da177e4SLinus Torvalds return 0;
3071da177e4SLinus Torvalds }
3081da177e4SLinus Torvalds
3091da177e4SLinus Torvalds
seq_free_client(struct snd_seq_client * client)310c7e0b5bfSTakashi Iwai static void seq_free_client(struct snd_seq_client * client)
3111da177e4SLinus Torvalds {
3121a60d4c5SIngo Molnar mutex_lock(®ister_mutex);
3131da177e4SLinus Torvalds switch (client->type) {
3141da177e4SLinus Torvalds case NO_CLIENT:
31504cc79a0STakashi Iwai pr_warn("ALSA: seq: Trying to free unused client %d\n",
316c7e0b5bfSTakashi Iwai client->number);
3171da177e4SLinus Torvalds break;
3181da177e4SLinus Torvalds case USER_CLIENT:
3191da177e4SLinus Torvalds case KERNEL_CLIENT:
3201da177e4SLinus Torvalds seq_free_client1(client);
3211da177e4SLinus Torvalds usage_free(&client_usage, 1);
3221da177e4SLinus Torvalds break;
3231da177e4SLinus Torvalds
3241da177e4SLinus Torvalds default:
32504cc79a0STakashi Iwai pr_err("ALSA: seq: Trying to free client %d with undefined type = %d\n",
326c7e0b5bfSTakashi Iwai client->number, client->type);
3271da177e4SLinus Torvalds }
3281a60d4c5SIngo Molnar mutex_unlock(®ister_mutex);
3291da177e4SLinus Torvalds
3301da177e4SLinus Torvalds snd_seq_system_client_ev_client_exit(client->number);
3311da177e4SLinus Torvalds }
3321da177e4SLinus Torvalds
3331da177e4SLinus Torvalds
3341da177e4SLinus Torvalds
3351da177e4SLinus Torvalds /* -------------------------------------------------------- */
3361da177e4SLinus Torvalds
3371da177e4SLinus Torvalds /* create a user client */
snd_seq_open(struct inode * inode,struct file * file)3381da177e4SLinus Torvalds static int snd_seq_open(struct inode *inode, struct file *file)
3391da177e4SLinus Torvalds {
3401da177e4SLinus Torvalds int c, mode; /* client id */
341c7e0b5bfSTakashi Iwai struct snd_seq_client *client;
342c7e0b5bfSTakashi Iwai struct snd_seq_user_client *user;
34302f4865fSTakashi Iwai int err;
34402f4865fSTakashi Iwai
345c5bf68feSKirill Smelkov err = stream_open(inode, file);
34602f4865fSTakashi Iwai if (err < 0)
34702f4865fSTakashi Iwai return err;
3481da177e4SLinus Torvalds
34904702e8dSTakashi Iwai mutex_lock(®ister_mutex);
350aa1e77e6SClemens Ladisch client = seq_create_client1(-1, SNDRV_SEQ_DEFAULT_EVENTS);
35104702e8dSTakashi Iwai if (!client) {
3521a60d4c5SIngo Molnar mutex_unlock(®ister_mutex);
3531da177e4SLinus Torvalds return -ENOMEM; /* failure code */
3541da177e4SLinus Torvalds }
3551da177e4SLinus Torvalds
3561da177e4SLinus Torvalds mode = snd_seq_file_flags(file);
3571da177e4SLinus Torvalds if (mode & SNDRV_SEQ_LFLG_INPUT)
3581da177e4SLinus Torvalds client->accept_input = 1;
3591da177e4SLinus Torvalds if (mode & SNDRV_SEQ_LFLG_OUTPUT)
3601da177e4SLinus Torvalds client->accept_output = 1;
3611da177e4SLinus Torvalds
3621da177e4SLinus Torvalds user = &client->data.user;
3631da177e4SLinus Torvalds user->fifo = NULL;
3641da177e4SLinus Torvalds user->fifo_pool_size = 0;
3651da177e4SLinus Torvalds
3661da177e4SLinus Torvalds if (mode & SNDRV_SEQ_LFLG_INPUT) {
3671da177e4SLinus Torvalds user->fifo_pool_size = SNDRV_SEQ_DEFAULT_CLIENT_EVENTS;
3681da177e4SLinus Torvalds user->fifo = snd_seq_fifo_new(user->fifo_pool_size);
3691da177e4SLinus Torvalds if (user->fifo == NULL) {
3701da177e4SLinus Torvalds seq_free_client1(client);
3711da177e4SLinus Torvalds kfree(client);
3721a60d4c5SIngo Molnar mutex_unlock(®ister_mutex);
3731da177e4SLinus Torvalds return -ENOMEM;
3741da177e4SLinus Torvalds }
3751da177e4SLinus Torvalds }
3761da177e4SLinus Torvalds
3771da177e4SLinus Torvalds usage_alloc(&client_usage, 1);
3781da177e4SLinus Torvalds client->type = USER_CLIENT;
3791a60d4c5SIngo Molnar mutex_unlock(®ister_mutex);
3801da177e4SLinus Torvalds
3811da177e4SLinus Torvalds c = client->number;
3821da177e4SLinus Torvalds file->private_data = client;
3831da177e4SLinus Torvalds
3841da177e4SLinus Torvalds /* fill client data */
3851da177e4SLinus Torvalds user->file = file;
3861da177e4SLinus Torvalds sprintf(client->name, "Client-%d", c);
387a1ce94d0SMartin Koegler client->data.user.owner = get_pid(task_pid(current));
3881da177e4SLinus Torvalds
3891da177e4SLinus Torvalds /* make others aware this new client */
3901da177e4SLinus Torvalds snd_seq_system_client_ev_client_start(c);
3911da177e4SLinus Torvalds
3921da177e4SLinus Torvalds return 0;
3931da177e4SLinus Torvalds }
3941da177e4SLinus Torvalds
3951da177e4SLinus Torvalds /* delete a user client */
snd_seq_release(struct inode * inode,struct file * file)3961da177e4SLinus Torvalds static int snd_seq_release(struct inode *inode, struct file *file)
3971da177e4SLinus Torvalds {
398c7e0b5bfSTakashi Iwai struct snd_seq_client *client = file->private_data;
3991da177e4SLinus Torvalds
4001da177e4SLinus Torvalds if (client) {
4011da177e4SLinus Torvalds seq_free_client(client);
4021da177e4SLinus Torvalds if (client->data.user.fifo)
4031da177e4SLinus Torvalds snd_seq_fifo_delete(&client->data.user.fifo);
404d2d247e3STakashi Iwai #if IS_ENABLED(CONFIG_SND_SEQ_UMP)
405d2d247e3STakashi Iwai free_ump_info(client);
406d2d247e3STakashi Iwai #endif
407a1ce94d0SMartin Koegler put_pid(client->data.user.owner);
4081da177e4SLinus Torvalds kfree(client);
4091da177e4SLinus Torvalds }
4101da177e4SLinus Torvalds
4111da177e4SLinus Torvalds return 0;
4121da177e4SLinus Torvalds }
4131da177e4SLinus Torvalds
event_is_compatible(const struct snd_seq_client * client,const struct snd_seq_event * ev)41446397622STakashi Iwai static bool event_is_compatible(const struct snd_seq_client *client,
41546397622STakashi Iwai const struct snd_seq_event *ev)
41646397622STakashi Iwai {
41746397622STakashi Iwai if (snd_seq_ev_is_ump(ev) && !client->midi_version)
41846397622STakashi Iwai return false;
41946397622STakashi Iwai if (snd_seq_ev_is_ump(ev) && snd_seq_ev_is_variable(ev))
42046397622STakashi Iwai return false;
42146397622STakashi Iwai return true;
42246397622STakashi Iwai }
4231da177e4SLinus Torvalds
4241da177e4SLinus Torvalds /* handle client read() */
4251da177e4SLinus Torvalds /* possible error values:
4261da177e4SLinus Torvalds * -ENXIO invalid client or file open mode
4271da177e4SLinus Torvalds * -ENOSPC FIFO overflow (the flag is cleared after this error report)
4281da177e4SLinus Torvalds * -EINVAL no enough user-space buffer to write the whole event
4291da177e4SLinus Torvalds * -EFAULT seg. fault during copy to user space
4301da177e4SLinus Torvalds */
snd_seq_read(struct file * file,char __user * buf,size_t count,loff_t * offset)431c7e0b5bfSTakashi Iwai static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count,
432c7e0b5bfSTakashi Iwai loff_t *offset)
4331da177e4SLinus Torvalds {
434c7e0b5bfSTakashi Iwai struct snd_seq_client *client = file->private_data;
435c7e0b5bfSTakashi Iwai struct snd_seq_fifo *fifo;
43646397622STakashi Iwai size_t aligned_size;
4371da177e4SLinus Torvalds int err;
4381da177e4SLinus Torvalds long result = 0;
439c7e0b5bfSTakashi Iwai struct snd_seq_event_cell *cell;
4401da177e4SLinus Torvalds
4411da177e4SLinus Torvalds if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT))
4421da177e4SLinus Torvalds return -ENXIO;
4431da177e4SLinus Torvalds
44496d4f267SLinus Torvalds if (!access_ok(buf, count))
4451da177e4SLinus Torvalds return -EFAULT;
4461da177e4SLinus Torvalds
4471da177e4SLinus Torvalds /* check client structures are in place */
4487eaa943cSTakashi Iwai if (snd_BUG_ON(!client))
4497eaa943cSTakashi Iwai return -ENXIO;
4501da177e4SLinus Torvalds
451f9a6bb84STakashi Iwai if (!client->accept_input)
452f9a6bb84STakashi Iwai return -ENXIO;
453f9a6bb84STakashi Iwai fifo = client->data.user.fifo;
454f9a6bb84STakashi Iwai if (!fifo)
4551da177e4SLinus Torvalds return -ENXIO;
4561da177e4SLinus Torvalds
4571da177e4SLinus Torvalds if (atomic_read(&fifo->overflow) > 0) {
4581da177e4SLinus Torvalds /* buffer overflow is detected */
4591da177e4SLinus Torvalds snd_seq_fifo_clear(fifo);
4601da177e4SLinus Torvalds /* return error code */
4611da177e4SLinus Torvalds return -ENOSPC;
4621da177e4SLinus Torvalds }
4631da177e4SLinus Torvalds
4641da177e4SLinus Torvalds cell = NULL;
4651da177e4SLinus Torvalds err = 0;
4661da177e4SLinus Torvalds snd_seq_fifo_lock(fifo);
4671da177e4SLinus Torvalds
4688c15a183STakashi Iwai if (IS_ENABLED(CONFIG_SND_SEQ_UMP) && client->midi_version > 0)
46946397622STakashi Iwai aligned_size = sizeof(struct snd_seq_ump_event);
47046397622STakashi Iwai else
47146397622STakashi Iwai aligned_size = sizeof(struct snd_seq_event);
47246397622STakashi Iwai
4731da177e4SLinus Torvalds /* while data available in queue */
47446397622STakashi Iwai while (count >= aligned_size) {
4751da177e4SLinus Torvalds int nonblock;
4761da177e4SLinus Torvalds
4771da177e4SLinus Torvalds nonblock = (file->f_flags & O_NONBLOCK) || result > 0;
478f9a6bb84STakashi Iwai err = snd_seq_fifo_cell_out(fifo, &cell, nonblock);
479f9a6bb84STakashi Iwai if (err < 0)
4801da177e4SLinus Torvalds break;
48146397622STakashi Iwai if (!event_is_compatible(client, &cell->event)) {
48246397622STakashi Iwai snd_seq_cell_free(cell);
48346397622STakashi Iwai cell = NULL;
48446397622STakashi Iwai continue;
48546397622STakashi Iwai }
4861da177e4SLinus Torvalds if (snd_seq_ev_is_variable(&cell->event)) {
48746397622STakashi Iwai struct snd_seq_ump_event tmpev;
48846397622STakashi Iwai
48946397622STakashi Iwai memcpy(&tmpev, &cell->event, aligned_size);
4901da177e4SLinus Torvalds tmpev.data.ext.len &= ~SNDRV_SEQ_EXT_MASK;
49146397622STakashi Iwai if (copy_to_user(buf, &tmpev, aligned_size)) {
4921da177e4SLinus Torvalds err = -EFAULT;
4931da177e4SLinus Torvalds break;
4941da177e4SLinus Torvalds }
49546397622STakashi Iwai count -= aligned_size;
49646397622STakashi Iwai buf += aligned_size;
4974d23359bSClemens Ladisch err = snd_seq_expand_var_event(&cell->event, count,
4984d23359bSClemens Ladisch (char __force *)buf, 0,
49946397622STakashi Iwai aligned_size);
5001da177e4SLinus Torvalds if (err < 0)
5011da177e4SLinus Torvalds break;
5021da177e4SLinus Torvalds result += err;
5031da177e4SLinus Torvalds count -= err;
5041da177e4SLinus Torvalds buf += err;
5051da177e4SLinus Torvalds } else {
50646397622STakashi Iwai if (copy_to_user(buf, &cell->event, aligned_size)) {
5071da177e4SLinus Torvalds err = -EFAULT;
5081da177e4SLinus Torvalds break;
5091da177e4SLinus Torvalds }
51046397622STakashi Iwai count -= aligned_size;
51146397622STakashi Iwai buf += aligned_size;
5121da177e4SLinus Torvalds }
5131da177e4SLinus Torvalds snd_seq_cell_free(cell);
5141da177e4SLinus Torvalds cell = NULL; /* to be sure */
51546397622STakashi Iwai result += aligned_size;
5161da177e4SLinus Torvalds }
5171da177e4SLinus Torvalds
5181da177e4SLinus Torvalds if (err < 0) {
5191da177e4SLinus Torvalds if (cell)
5201da177e4SLinus Torvalds snd_seq_fifo_cell_putback(fifo, cell);
5211da177e4SLinus Torvalds if (err == -EAGAIN && result > 0)
5221da177e4SLinus Torvalds err = 0;
5231da177e4SLinus Torvalds }
5241da177e4SLinus Torvalds snd_seq_fifo_unlock(fifo);
5251da177e4SLinus Torvalds
5261da177e4SLinus Torvalds return (err < 0) ? err : result;
5271da177e4SLinus Torvalds }
5281da177e4SLinus Torvalds
5291da177e4SLinus Torvalds
5301da177e4SLinus Torvalds /*
5311da177e4SLinus Torvalds * check access permission to the port
5321da177e4SLinus Torvalds */
check_port_perm(struct snd_seq_client_port * port,unsigned int flags)533c7e0b5bfSTakashi Iwai static int check_port_perm(struct snd_seq_client_port *port, unsigned int flags)
5341da177e4SLinus Torvalds {
5351da177e4SLinus Torvalds if ((port->capability & flags) != flags)
5361da177e4SLinus Torvalds return 0;
5371da177e4SLinus Torvalds return flags;
5381da177e4SLinus Torvalds }
5391da177e4SLinus Torvalds
5401da177e4SLinus Torvalds /*
5411da177e4SLinus Torvalds * check if the destination client is available, and return the pointer
5421da177e4SLinus Torvalds * if filter is non-zero, client filter bitmap is tested.
5431da177e4SLinus Torvalds */
get_event_dest_client(struct snd_seq_event * event,int filter)544c7e0b5bfSTakashi Iwai static struct snd_seq_client *get_event_dest_client(struct snd_seq_event *event,
545c7e0b5bfSTakashi Iwai int filter)
5461da177e4SLinus Torvalds {
547c7e0b5bfSTakashi Iwai struct snd_seq_client *dest;
5481da177e4SLinus Torvalds
5491da177e4SLinus Torvalds dest = snd_seq_client_use_ptr(event->dest.client);
5501da177e4SLinus Torvalds if (dest == NULL)
5511da177e4SLinus Torvalds return NULL;
5521da177e4SLinus Torvalds if (! dest->accept_input)
5531da177e4SLinus Torvalds goto __not_avail;
554b1922c31STakashi Iwai if (snd_seq_ev_is_ump(event))
555b1922c31STakashi Iwai return dest; /* ok - no filter checks */
556b1922c31STakashi Iwai
5571da177e4SLinus Torvalds if ((dest->filter & SNDRV_SEQ_FILTER_USE_EVENT) &&
5581da177e4SLinus Torvalds ! test_bit(event->type, dest->event_filter))
5591da177e4SLinus Torvalds goto __not_avail;
5601da177e4SLinus Torvalds if (filter && !(dest->filter & filter))
5611da177e4SLinus Torvalds goto __not_avail;
5621da177e4SLinus Torvalds
5631da177e4SLinus Torvalds return dest; /* ok - accessible */
5641da177e4SLinus Torvalds __not_avail:
5651da177e4SLinus Torvalds snd_seq_client_unlock(dest);
5661da177e4SLinus Torvalds return NULL;
5671da177e4SLinus Torvalds }
5681da177e4SLinus Torvalds
5691da177e4SLinus Torvalds
5701da177e4SLinus Torvalds /*
5711da177e4SLinus Torvalds * Return the error event.
5721da177e4SLinus Torvalds *
5731da177e4SLinus Torvalds * If the receiver client is a user client, the original event is
5741da177e4SLinus Torvalds * encapsulated in SNDRV_SEQ_EVENT_BOUNCE as variable length event. If
5751da177e4SLinus Torvalds * the original event is also variable length, the external data is
5761da177e4SLinus Torvalds * copied after the event record.
5771da177e4SLinus Torvalds * If the receiver client is a kernel client, the original event is
5781da177e4SLinus Torvalds * quoted in SNDRV_SEQ_EVENT_KERNEL_ERROR, since this requires no extra
5791da177e4SLinus Torvalds * kmalloc.
5801da177e4SLinus Torvalds */
bounce_error_event(struct snd_seq_client * client,struct snd_seq_event * event,int err,int atomic,int hop)581c7e0b5bfSTakashi Iwai static int bounce_error_event(struct snd_seq_client *client,
582c7e0b5bfSTakashi Iwai struct snd_seq_event *event,
5831da177e4SLinus Torvalds int err, int atomic, int hop)
5841da177e4SLinus Torvalds {
585c7e0b5bfSTakashi Iwai struct snd_seq_event bounce_ev;
5861da177e4SLinus Torvalds int result;
5871da177e4SLinus Torvalds
5881da177e4SLinus Torvalds if (client == NULL ||
5891da177e4SLinus Torvalds ! (client->filter & SNDRV_SEQ_FILTER_BOUNCE) ||
5901da177e4SLinus Torvalds ! client->accept_input)
5911da177e4SLinus Torvalds return 0; /* ignored */
5921da177e4SLinus Torvalds
5931da177e4SLinus Torvalds /* set up quoted error */
5941da177e4SLinus Torvalds memset(&bounce_ev, 0, sizeof(bounce_ev));
5951da177e4SLinus Torvalds bounce_ev.type = SNDRV_SEQ_EVENT_KERNEL_ERROR;
5961da177e4SLinus Torvalds bounce_ev.flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
5971da177e4SLinus Torvalds bounce_ev.queue = SNDRV_SEQ_QUEUE_DIRECT;
5981da177e4SLinus Torvalds bounce_ev.source.client = SNDRV_SEQ_CLIENT_SYSTEM;
5991da177e4SLinus Torvalds bounce_ev.source.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
6001da177e4SLinus Torvalds bounce_ev.dest.client = client->number;
6011da177e4SLinus Torvalds bounce_ev.dest.port = event->source.port;
6021da177e4SLinus Torvalds bounce_ev.data.quote.origin = event->dest;
6031da177e4SLinus Torvalds bounce_ev.data.quote.event = event;
6041da177e4SLinus Torvalds bounce_ev.data.quote.value = -err; /* use positive value */
6051da177e4SLinus Torvalds result = snd_seq_deliver_single_event(NULL, &bounce_ev, 0, atomic, hop + 1);
6061da177e4SLinus Torvalds if (result < 0) {
6071da177e4SLinus Torvalds client->event_lost++;
6081da177e4SLinus Torvalds return result;
6091da177e4SLinus Torvalds }
6101da177e4SLinus Torvalds
6111da177e4SLinus Torvalds return result;
6121da177e4SLinus Torvalds }
6131da177e4SLinus Torvalds
6141da177e4SLinus Torvalds
6151da177e4SLinus Torvalds /*
6161da177e4SLinus Torvalds * rewrite the time-stamp of the event record with the curren time
6171da177e4SLinus Torvalds * of the given queue.
6181da177e4SLinus Torvalds * return non-zero if updated.
6191da177e4SLinus Torvalds */
update_timestamp_of_queue(struct snd_seq_event * event,int queue,int real_time)620c7e0b5bfSTakashi Iwai static int update_timestamp_of_queue(struct snd_seq_event *event,
621c7e0b5bfSTakashi Iwai int queue, int real_time)
6221da177e4SLinus Torvalds {
623c7e0b5bfSTakashi Iwai struct snd_seq_queue *q;
6241da177e4SLinus Torvalds
6251da177e4SLinus Torvalds q = queueptr(queue);
6261da177e4SLinus Torvalds if (! q)
6271da177e4SLinus Torvalds return 0;
6281da177e4SLinus Torvalds event->queue = queue;
6291da177e4SLinus Torvalds event->flags &= ~SNDRV_SEQ_TIME_STAMP_MASK;
6301da177e4SLinus Torvalds if (real_time) {
631dc749779STakashi Iwai event->time.time = snd_seq_timer_get_cur_time(q->timer, true);
6321da177e4SLinus Torvalds event->flags |= SNDRV_SEQ_TIME_STAMP_REAL;
6331da177e4SLinus Torvalds } else {
6341da177e4SLinus Torvalds event->time.tick = snd_seq_timer_get_cur_tick(q->timer);
6351da177e4SLinus Torvalds event->flags |= SNDRV_SEQ_TIME_STAMP_TICK;
6361da177e4SLinus Torvalds }
6371da177e4SLinus Torvalds queuefree(q);
6381da177e4SLinus Torvalds return 1;
6391da177e4SLinus Torvalds }
6401da177e4SLinus Torvalds
641e9e02819STakashi Iwai /* deliver a single event; called from below and UMP converter */
__snd_seq_deliver_single_event(struct snd_seq_client * dest,struct snd_seq_client_port * dest_port,struct snd_seq_event * event,int atomic,int hop)642e9e02819STakashi Iwai int __snd_seq_deliver_single_event(struct snd_seq_client *dest,
643e9e02819STakashi Iwai struct snd_seq_client_port *dest_port,
644e9e02819STakashi Iwai struct snd_seq_event *event,
645e9e02819STakashi Iwai int atomic, int hop)
646e9e02819STakashi Iwai {
647e9e02819STakashi Iwai switch (dest->type) {
648e9e02819STakashi Iwai case USER_CLIENT:
649e9e02819STakashi Iwai if (!dest->data.user.fifo)
650e9e02819STakashi Iwai return 0;
651e9e02819STakashi Iwai return snd_seq_fifo_event_in(dest->data.user.fifo, event);
652e9e02819STakashi Iwai case KERNEL_CLIENT:
653e9e02819STakashi Iwai if (!dest_port->event_input)
654e9e02819STakashi Iwai return 0;
655e9e02819STakashi Iwai return dest_port->event_input(event,
656e9e02819STakashi Iwai snd_seq_ev_is_direct(event),
657e9e02819STakashi Iwai dest_port->private_data,
658e9e02819STakashi Iwai atomic, hop);
659e9e02819STakashi Iwai }
660e9e02819STakashi Iwai return 0;
661e9e02819STakashi Iwai }
6621da177e4SLinus Torvalds
6631da177e4SLinus Torvalds /*
6641da177e4SLinus Torvalds * deliver an event to the specified destination.
6651da177e4SLinus Torvalds * if filter is non-zero, client filter bitmap is tested.
6661da177e4SLinus Torvalds *
6671da177e4SLinus Torvalds * RETURN VALUE: 0 : if succeeded
6681da177e4SLinus Torvalds * <0 : error
6691da177e4SLinus Torvalds */
snd_seq_deliver_single_event(struct snd_seq_client * client,struct snd_seq_event * event,int filter,int atomic,int hop)670c7e0b5bfSTakashi Iwai static int snd_seq_deliver_single_event(struct snd_seq_client *client,
671c7e0b5bfSTakashi Iwai struct snd_seq_event *event,
6721da177e4SLinus Torvalds int filter, int atomic, int hop)
6731da177e4SLinus Torvalds {
674c7e0b5bfSTakashi Iwai struct snd_seq_client *dest = NULL;
675c7e0b5bfSTakashi Iwai struct snd_seq_client_port *dest_port = NULL;
6761da177e4SLinus Torvalds int result = -ENOENT;
6771da177e4SLinus Torvalds int direct;
6781da177e4SLinus Torvalds
6791da177e4SLinus Torvalds direct = snd_seq_ev_is_direct(event);
6801da177e4SLinus Torvalds
6811da177e4SLinus Torvalds dest = get_event_dest_client(event, filter);
6821da177e4SLinus Torvalds if (dest == NULL)
6831da177e4SLinus Torvalds goto __skip;
6841da177e4SLinus Torvalds dest_port = snd_seq_port_use_ptr(dest, event->dest.port);
6851da177e4SLinus Torvalds if (dest_port == NULL)
6861da177e4SLinus Torvalds goto __skip;
6871da177e4SLinus Torvalds
6881da177e4SLinus Torvalds /* check permission */
6891da177e4SLinus Torvalds if (! check_port_perm(dest_port, SNDRV_SEQ_PORT_CAP_WRITE)) {
6901da177e4SLinus Torvalds result = -EPERM;
6911da177e4SLinus Torvalds goto __skip;
6921da177e4SLinus Torvalds }
6931da177e4SLinus Torvalds
6941da177e4SLinus Torvalds if (dest_port->timestamping)
6951da177e4SLinus Torvalds update_timestamp_of_queue(event, dest_port->time_queue,
6961da177e4SLinus Torvalds dest_port->time_real);
6971da177e4SLinus Torvalds
698e9e02819STakashi Iwai #if IS_ENABLED(CONFIG_SND_SEQ_UMP)
699e9e02819STakashi Iwai if (snd_seq_ev_is_ump(event)) {
70048e348ffSTakashi Iwai if (!(dest->filter & SNDRV_SEQ_FILTER_NO_CONVERT)) {
701e9e02819STakashi Iwai result = snd_seq_deliver_from_ump(client, dest, dest_port,
702e9e02819STakashi Iwai event, atomic, hop);
703e9e02819STakashi Iwai goto __skip;
70448e348ffSTakashi Iwai } else if (dest->type == USER_CLIENT &&
70548e348ffSTakashi Iwai !snd_seq_client_is_ump(dest)) {
70648e348ffSTakashi Iwai result = 0; // drop the event
70748e348ffSTakashi Iwai goto __skip;
70848e348ffSTakashi Iwai }
709e9e02819STakashi Iwai } else if (snd_seq_client_is_ump(dest)) {
71048e348ffSTakashi Iwai if (!(dest->filter & SNDRV_SEQ_FILTER_NO_CONVERT)) {
711e9e02819STakashi Iwai result = snd_seq_deliver_to_ump(client, dest, dest_port,
712e9e02819STakashi Iwai event, atomic, hop);
713e9e02819STakashi Iwai goto __skip;
7141da177e4SLinus Torvalds }
715329ffe11STakashi Iwai }
716e9e02819STakashi Iwai #endif /* CONFIG_SND_SEQ_UMP */
717e9e02819STakashi Iwai
718e9e02819STakashi Iwai result = __snd_seq_deliver_single_event(dest, dest_port, event,
719e9e02819STakashi Iwai atomic, hop);
7201da177e4SLinus Torvalds
7211da177e4SLinus Torvalds __skip:
7221da177e4SLinus Torvalds if (dest_port)
7231da177e4SLinus Torvalds snd_seq_port_unlock(dest_port);
7241da177e4SLinus Torvalds if (dest)
7251da177e4SLinus Torvalds snd_seq_client_unlock(dest);
7261da177e4SLinus Torvalds
7271da177e4SLinus Torvalds if (result < 0 && !direct) {
7281da177e4SLinus Torvalds result = bounce_error_event(client, event, result, atomic, hop);
7291da177e4SLinus Torvalds }
7301da177e4SLinus Torvalds return result;
7311da177e4SLinus Torvalds }
7321da177e4SLinus Torvalds
7331da177e4SLinus Torvalds
7341da177e4SLinus Torvalds /*
7351da177e4SLinus Torvalds * send the event to all subscribers:
7361da177e4SLinus Torvalds */
__deliver_to_subscribers(struct snd_seq_client * client,struct snd_seq_event * event,struct snd_seq_client_port * src_port,int atomic,int hop)737177ccf81STakashi Iwai static int __deliver_to_subscribers(struct snd_seq_client *client,
738c7e0b5bfSTakashi Iwai struct snd_seq_event *event,
739177ccf81STakashi Iwai struct snd_seq_client_port *src_port,
7401da177e4SLinus Torvalds int atomic, int hop)
7411da177e4SLinus Torvalds {
742c7e0b5bfSTakashi Iwai struct snd_seq_subscribers *subs;
74327423257SAdam Goode int err, result = 0, num_ev = 0;
74446397622STakashi Iwai union __snd_seq_event event_saved;
74546397622STakashi Iwai size_t saved_size;
746c7e0b5bfSTakashi Iwai struct snd_seq_port_subs_info *grp;
7471da177e4SLinus Torvalds
7481da177e4SLinus Torvalds /* save original event record */
74946397622STakashi Iwai saved_size = snd_seq_event_packet_size(event);
75046397622STakashi Iwai memcpy(&event_saved, event, saved_size);
7511da177e4SLinus Torvalds grp = &src_port->c_src;
7521da177e4SLinus Torvalds
7531da177e4SLinus Torvalds /* lock list */
7541da177e4SLinus Torvalds if (atomic)
7551da177e4SLinus Torvalds read_lock(&grp->list_lock);
7561da177e4SLinus Torvalds else
7571f20f9ffSTakashi Iwai down_read_nested(&grp->list_mutex, hop);
7589244b2c3SJohannes Berg list_for_each_entry(subs, &grp->list_head, src_list) {
7597f0973e9STakashi Iwai /* both ports ready? */
7607f0973e9STakashi Iwai if (atomic_read(&subs->ref_count) != 2)
7617f0973e9STakashi Iwai continue;
7621da177e4SLinus Torvalds event->dest = subs->info.dest;
7631da177e4SLinus Torvalds if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP)
7641da177e4SLinus Torvalds /* convert time according to flag with subscription */
7651da177e4SLinus Torvalds update_timestamp_of_queue(event, subs->info.queue,
7661da177e4SLinus Torvalds subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL);
7671da177e4SLinus Torvalds err = snd_seq_deliver_single_event(client, event,
7681da177e4SLinus Torvalds 0, atomic, hop);
76927423257SAdam Goode if (err < 0) {
77027423257SAdam Goode /* save first error that occurs and continue */
77127423257SAdam Goode if (!result)
77227423257SAdam Goode result = err;
77327423257SAdam Goode continue;
77427423257SAdam Goode }
7751da177e4SLinus Torvalds num_ev++;
7761da177e4SLinus Torvalds /* restore original event record */
77746397622STakashi Iwai memcpy(event, &event_saved, saved_size);
7781da177e4SLinus Torvalds }
7791da177e4SLinus Torvalds if (atomic)
7801da177e4SLinus Torvalds read_unlock(&grp->list_lock);
7811da177e4SLinus Torvalds else
7821da177e4SLinus Torvalds up_read(&grp->list_mutex);
78346397622STakashi Iwai memcpy(event, &event_saved, saved_size);
78427423257SAdam Goode return (result < 0) ? result : num_ev;
7851da177e4SLinus Torvalds }
7861da177e4SLinus Torvalds
deliver_to_subscribers(struct snd_seq_client * client,struct snd_seq_event * event,int atomic,int hop)787177ccf81STakashi Iwai static int deliver_to_subscribers(struct snd_seq_client *client,
788177ccf81STakashi Iwai struct snd_seq_event *event,
789177ccf81STakashi Iwai int atomic, int hop)
790177ccf81STakashi Iwai {
791177ccf81STakashi Iwai struct snd_seq_client_port *src_port;
792177ccf81STakashi Iwai int ret = 0, ret2;
793177ccf81STakashi Iwai
794177ccf81STakashi Iwai src_port = snd_seq_port_use_ptr(client, event->source.port);
795177ccf81STakashi Iwai if (src_port) {
796177ccf81STakashi Iwai ret = __deliver_to_subscribers(client, event, src_port, atomic, hop);
797177ccf81STakashi Iwai snd_seq_port_unlock(src_port);
798177ccf81STakashi Iwai }
799177ccf81STakashi Iwai
800177ccf81STakashi Iwai if (client->ump_endpoint_port < 0 ||
801177ccf81STakashi Iwai event->source.port == client->ump_endpoint_port)
802177ccf81STakashi Iwai return ret;
803177ccf81STakashi Iwai
804177ccf81STakashi Iwai src_port = snd_seq_port_use_ptr(client, client->ump_endpoint_port);
805177ccf81STakashi Iwai if (!src_port)
806177ccf81STakashi Iwai return ret;
807177ccf81STakashi Iwai ret2 = __deliver_to_subscribers(client, event, src_port, atomic, hop);
808177ccf81STakashi Iwai snd_seq_port_unlock(src_port);
809177ccf81STakashi Iwai return ret2 < 0 ? ret2 : ret;
810177ccf81STakashi Iwai }
811177ccf81STakashi Iwai
8121da177e4SLinus Torvalds /* deliver an event to the destination port(s).
8131da177e4SLinus Torvalds * if the event is to subscribers or broadcast, the event is dispatched
8141da177e4SLinus Torvalds * to multiple targets.
8151da177e4SLinus Torvalds *
8161da177e4SLinus Torvalds * RETURN VALUE: n > 0 : the number of delivered events.
8171da177e4SLinus Torvalds * n == 0 : the event was not passed to any client.
8181da177e4SLinus Torvalds * n < 0 : error - event was not processed.
8191da177e4SLinus Torvalds */
snd_seq_deliver_event(struct snd_seq_client * client,struct snd_seq_event * event,int atomic,int hop)820c7e0b5bfSTakashi Iwai static int snd_seq_deliver_event(struct snd_seq_client *client, struct snd_seq_event *event,
8211da177e4SLinus Torvalds int atomic, int hop)
8221da177e4SLinus Torvalds {
8231da177e4SLinus Torvalds int result;
8241da177e4SLinus Torvalds
8251da177e4SLinus Torvalds hop++;
8261da177e4SLinus Torvalds if (hop >= SNDRV_SEQ_MAX_HOPS) {
82704cc79a0STakashi Iwai pr_debug("ALSA: seq: too long delivery path (%d:%d->%d:%d)\n",
8281da177e4SLinus Torvalds event->source.client, event->source.port,
8291da177e4SLinus Torvalds event->dest.client, event->dest.port);
8301da177e4SLinus Torvalds return -EMLINK;
8311da177e4SLinus Torvalds }
8321da177e4SLinus Torvalds
83319b592daSTakashi Iwai if (snd_seq_ev_is_variable(event) &&
83419b592daSTakashi Iwai snd_BUG_ON(atomic && (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR)))
83519b592daSTakashi Iwai return -EINVAL;
83619b592daSTakashi Iwai
8371da177e4SLinus Torvalds if (event->queue == SNDRV_SEQ_ADDRESS_SUBSCRIBERS ||
8381da177e4SLinus Torvalds event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS)
8391da177e4SLinus Torvalds result = deliver_to_subscribers(client, event, atomic, hop);
8401da177e4SLinus Torvalds else
8411da177e4SLinus Torvalds result = snd_seq_deliver_single_event(client, event, 0, atomic, hop);
8421da177e4SLinus Torvalds
8431da177e4SLinus Torvalds return result;
8441da177e4SLinus Torvalds }
8451da177e4SLinus Torvalds
8461da177e4SLinus Torvalds /*
8471da177e4SLinus Torvalds * dispatch an event cell:
8481da177e4SLinus Torvalds * This function is called only from queue check routines in timer
8491da177e4SLinus Torvalds * interrupts or after enqueued.
8501da177e4SLinus Torvalds * The event cell shall be released or re-queued in this function.
8511da177e4SLinus Torvalds *
8521da177e4SLinus Torvalds * RETURN VALUE: n > 0 : the number of delivered events.
8531da177e4SLinus Torvalds * n == 0 : the event was not passed to any client.
8541da177e4SLinus Torvalds * n < 0 : error - event was not processed.
8551da177e4SLinus Torvalds */
snd_seq_dispatch_event(struct snd_seq_event_cell * cell,int atomic,int hop)856c7e0b5bfSTakashi Iwai int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop)
8571da177e4SLinus Torvalds {
858c7e0b5bfSTakashi Iwai struct snd_seq_client *client;
8591da177e4SLinus Torvalds int result;
8601da177e4SLinus Torvalds
8617eaa943cSTakashi Iwai if (snd_BUG_ON(!cell))
8627eaa943cSTakashi Iwai return -EINVAL;
8631da177e4SLinus Torvalds
8641da177e4SLinus Torvalds client = snd_seq_client_use_ptr(cell->event.source.client);
8651da177e4SLinus Torvalds if (client == NULL) {
8661da177e4SLinus Torvalds snd_seq_cell_free(cell); /* release this cell */
8671da177e4SLinus Torvalds return -EINVAL;
8681da177e4SLinus Torvalds }
8691da177e4SLinus Torvalds
87046397622STakashi Iwai if (!snd_seq_ev_is_ump(&cell->event) &&
87146397622STakashi Iwai cell->event.type == SNDRV_SEQ_EVENT_NOTE) {
8721da177e4SLinus Torvalds /* NOTE event:
8731da177e4SLinus Torvalds * the event cell is re-used as a NOTE-OFF event and
8741da177e4SLinus Torvalds * enqueued again.
8751da177e4SLinus Torvalds */
876c7e0b5bfSTakashi Iwai struct snd_seq_event tmpev, *ev;
8771da177e4SLinus Torvalds
8781da177e4SLinus Torvalds /* reserve this event to enqueue note-off later */
8791da177e4SLinus Torvalds tmpev = cell->event;
8801da177e4SLinus Torvalds tmpev.type = SNDRV_SEQ_EVENT_NOTEON;
8811da177e4SLinus Torvalds result = snd_seq_deliver_event(client, &tmpev, atomic, hop);
8821da177e4SLinus Torvalds
8831da177e4SLinus Torvalds /*
8841da177e4SLinus Torvalds * This was originally a note event. We now re-use the
8851da177e4SLinus Torvalds * cell for the note-off event.
8861da177e4SLinus Torvalds */
8871da177e4SLinus Torvalds
8881da177e4SLinus Torvalds ev = &cell->event;
8891da177e4SLinus Torvalds ev->type = SNDRV_SEQ_EVENT_NOTEOFF;
8901da177e4SLinus Torvalds ev->flags |= SNDRV_SEQ_PRIORITY_HIGH;
8911da177e4SLinus Torvalds
8921da177e4SLinus Torvalds /* add the duration time */
8931da177e4SLinus Torvalds switch (ev->flags & SNDRV_SEQ_TIME_STAMP_MASK) {
8941da177e4SLinus Torvalds case SNDRV_SEQ_TIME_STAMP_TICK:
89546397622STakashi Iwai cell->event.time.tick += ev->data.note.duration;
8961da177e4SLinus Torvalds break;
8971da177e4SLinus Torvalds case SNDRV_SEQ_TIME_STAMP_REAL:
8981da177e4SLinus Torvalds /* unit for duration is ms */
8991da177e4SLinus Torvalds ev->time.time.tv_nsec += 1000000 * (ev->data.note.duration % 1000);
9001da177e4SLinus Torvalds ev->time.time.tv_sec += ev->data.note.duration / 1000 +
9011da177e4SLinus Torvalds ev->time.time.tv_nsec / 1000000000;
9021da177e4SLinus Torvalds ev->time.time.tv_nsec %= 1000000000;
9031da177e4SLinus Torvalds break;
9041da177e4SLinus Torvalds }
9051da177e4SLinus Torvalds ev->data.note.velocity = ev->data.note.off_velocity;
9061da177e4SLinus Torvalds
9071da177e4SLinus Torvalds /* Now queue this cell as the note off event */
9081da177e4SLinus Torvalds if (snd_seq_enqueue_event(cell, atomic, hop) < 0)
9091da177e4SLinus Torvalds snd_seq_cell_free(cell); /* release this cell */
9101da177e4SLinus Torvalds
9111da177e4SLinus Torvalds } else {
9121da177e4SLinus Torvalds /* Normal events:
9131da177e4SLinus Torvalds * event cell is freed after processing the event
9141da177e4SLinus Torvalds */
9151da177e4SLinus Torvalds
9161da177e4SLinus Torvalds result = snd_seq_deliver_event(client, &cell->event, atomic, hop);
9171da177e4SLinus Torvalds snd_seq_cell_free(cell);
9181da177e4SLinus Torvalds }
9191da177e4SLinus Torvalds
9201da177e4SLinus Torvalds snd_seq_client_unlock(client);
9211da177e4SLinus Torvalds return result;
9221da177e4SLinus Torvalds }
9231da177e4SLinus Torvalds
9241da177e4SLinus Torvalds
9251da177e4SLinus Torvalds /* Allocate a cell from client pool and enqueue it to queue:
9261da177e4SLinus Torvalds * if pool is empty and blocking is TRUE, sleep until a new cell is
9271da177e4SLinus Torvalds * available.
9281da177e4SLinus Torvalds */
snd_seq_client_enqueue_event(struct snd_seq_client * client,struct snd_seq_event * event,struct file * file,int blocking,int atomic,int hop,struct mutex * mutexp)929c7e0b5bfSTakashi Iwai static int snd_seq_client_enqueue_event(struct snd_seq_client *client,
930c7e0b5bfSTakashi Iwai struct snd_seq_event *event,
9311da177e4SLinus Torvalds struct file *file, int blocking,
9327bd80091STakashi Iwai int atomic, int hop,
9337bd80091STakashi Iwai struct mutex *mutexp)
9341da177e4SLinus Torvalds {
935c7e0b5bfSTakashi Iwai struct snd_seq_event_cell *cell;
9361da177e4SLinus Torvalds int err;
9371da177e4SLinus Torvalds
9381da177e4SLinus Torvalds /* special queue values - force direct passing */
9391da177e4SLinus Torvalds if (event->queue == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) {
9401da177e4SLinus Torvalds event->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
9411da177e4SLinus Torvalds event->queue = SNDRV_SEQ_QUEUE_DIRECT;
94294c5b717STakashi Iwai } else if (event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) {
9431da177e4SLinus Torvalds /* check presence of source port */
944c7e0b5bfSTakashi Iwai struct snd_seq_client_port *src_port = snd_seq_port_use_ptr(client, event->source.port);
9451da177e4SLinus Torvalds if (src_port == NULL)
9461da177e4SLinus Torvalds return -EINVAL;
9471da177e4SLinus Torvalds snd_seq_port_unlock(src_port);
9481da177e4SLinus Torvalds }
9491da177e4SLinus Torvalds
9501da177e4SLinus Torvalds /* direct event processing without enqueued */
9511da177e4SLinus Torvalds if (snd_seq_ev_is_direct(event)) {
95246397622STakashi Iwai if (!snd_seq_ev_is_ump(event) &&
95346397622STakashi Iwai event->type == SNDRV_SEQ_EVENT_NOTE)
9541da177e4SLinus Torvalds return -EINVAL; /* this event must be enqueued! */
9551da177e4SLinus Torvalds return snd_seq_deliver_event(client, event, atomic, hop);
9561da177e4SLinus Torvalds }
9571da177e4SLinus Torvalds
9581da177e4SLinus Torvalds /* Not direct, normal queuing */
9591da177e4SLinus Torvalds if (snd_seq_queue_is_used(event->queue, client->number) <= 0)
9601da177e4SLinus Torvalds return -EINVAL; /* invalid queue */
9611da177e4SLinus Torvalds if (! snd_seq_write_pool_allocated(client))
9621da177e4SLinus Torvalds return -ENXIO; /* queue is not allocated */
9631da177e4SLinus Torvalds
9641da177e4SLinus Torvalds /* allocate an event cell */
9657bd80091STakashi Iwai err = snd_seq_event_dup(client->pool, event, &cell, !blocking || atomic,
9667bd80091STakashi Iwai file, mutexp);
9671da177e4SLinus Torvalds if (err < 0)
9681da177e4SLinus Torvalds return err;
9691da177e4SLinus Torvalds
9701da177e4SLinus Torvalds /* we got a cell. enqueue it. */
971f9a6bb84STakashi Iwai err = snd_seq_enqueue_event(cell, atomic, hop);
972f9a6bb84STakashi Iwai if (err < 0) {
9731da177e4SLinus Torvalds snd_seq_cell_free(cell);
9741da177e4SLinus Torvalds return err;
9751da177e4SLinus Torvalds }
9761da177e4SLinus Torvalds
9771da177e4SLinus Torvalds return 0;
9781da177e4SLinus Torvalds }
9791da177e4SLinus Torvalds
9801da177e4SLinus Torvalds
9811da177e4SLinus Torvalds /*
9821da177e4SLinus Torvalds * check validity of event type and data length.
9831da177e4SLinus Torvalds * return non-zero if invalid.
9841da177e4SLinus Torvalds */
check_event_type_and_length(struct snd_seq_event * ev)985c7e0b5bfSTakashi Iwai static int check_event_type_and_length(struct snd_seq_event *ev)
9861da177e4SLinus Torvalds {
9871da177e4SLinus Torvalds switch (snd_seq_ev_length_type(ev)) {
9881da177e4SLinus Torvalds case SNDRV_SEQ_EVENT_LENGTH_FIXED:
9891da177e4SLinus Torvalds if (snd_seq_ev_is_variable_type(ev))
9901da177e4SLinus Torvalds return -EINVAL;
9911da177e4SLinus Torvalds break;
9921da177e4SLinus Torvalds case SNDRV_SEQ_EVENT_LENGTH_VARIABLE:
9931da177e4SLinus Torvalds if (! snd_seq_ev_is_variable_type(ev) ||
9941da177e4SLinus Torvalds (ev->data.ext.len & ~SNDRV_SEQ_EXT_MASK) >= SNDRV_SEQ_MAX_EVENT_LEN)
9951da177e4SLinus Torvalds return -EINVAL;
9961da177e4SLinus Torvalds break;
9971da177e4SLinus Torvalds case SNDRV_SEQ_EVENT_LENGTH_VARUSR:
998e5723b41STakashi Iwai if (! snd_seq_ev_is_direct(ev))
9991da177e4SLinus Torvalds return -EINVAL;
10001da177e4SLinus Torvalds break;
10011da177e4SLinus Torvalds }
10021da177e4SLinus Torvalds return 0;
10031da177e4SLinus Torvalds }
10041da177e4SLinus Torvalds
10051da177e4SLinus Torvalds
10061da177e4SLinus Torvalds /* handle write() */
10071da177e4SLinus Torvalds /* possible error values:
10081da177e4SLinus Torvalds * -ENXIO invalid client or file open mode
10091da177e4SLinus Torvalds * -ENOMEM malloc failed
10101da177e4SLinus Torvalds * -EFAULT seg. fault during copy from user space
10111da177e4SLinus Torvalds * -EINVAL invalid event
10121da177e4SLinus Torvalds * -EAGAIN no space in output pool
10131da177e4SLinus Torvalds * -EINTR interrupts while sleep
10141da177e4SLinus Torvalds * -EMLINK too many hops
10151da177e4SLinus Torvalds * others depends on return value from driver callback
10161da177e4SLinus Torvalds */
snd_seq_write(struct file * file,const char __user * buf,size_t count,loff_t * offset)1017c7e0b5bfSTakashi Iwai static ssize_t snd_seq_write(struct file *file, const char __user *buf,
1018c7e0b5bfSTakashi Iwai size_t count, loff_t *offset)
10191da177e4SLinus Torvalds {
1020c7e0b5bfSTakashi Iwai struct snd_seq_client *client = file->private_data;
10211da177e4SLinus Torvalds int written = 0, len;
1022ede34f39STakashi Iwai int err, handled;
102346397622STakashi Iwai union __snd_seq_event __event;
102446397622STakashi Iwai struct snd_seq_event *ev = &__event.legacy;
10251da177e4SLinus Torvalds
10261da177e4SLinus Torvalds if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT))
10271da177e4SLinus Torvalds return -ENXIO;
10281da177e4SLinus Torvalds
10291da177e4SLinus Torvalds /* check client structures are in place */
10307eaa943cSTakashi Iwai if (snd_BUG_ON(!client))
10317eaa943cSTakashi Iwai return -ENXIO;
10321da177e4SLinus Torvalds
10331da177e4SLinus Torvalds if (!client->accept_output || client->pool == NULL)
10341da177e4SLinus Torvalds return -ENXIO;
10351da177e4SLinus Torvalds
1036ede34f39STakashi Iwai repeat:
1037ede34f39STakashi Iwai handled = 0;
10381da177e4SLinus Torvalds /* allocate the pool now if the pool is not allocated yet */
1039d15d662eSTakashi Iwai mutex_lock(&client->ioctl_mutex);
10407bd80091STakashi Iwai if (client->pool->size > 0 && !snd_seq_write_pool_allocated(client)) {
1041d15d662eSTakashi Iwai err = snd_seq_pool_init(client->pool);
1042d15d662eSTakashi Iwai if (err < 0)
10437bd80091STakashi Iwai goto out;
10441da177e4SLinus Torvalds }
10451da177e4SLinus Torvalds
10461da177e4SLinus Torvalds /* only process whole events */
1047d15d662eSTakashi Iwai err = -EINVAL;
1048c7e0b5bfSTakashi Iwai while (count >= sizeof(struct snd_seq_event)) {
10491da177e4SLinus Torvalds /* Read in the event header from the user */
105046397622STakashi Iwai len = sizeof(struct snd_seq_event);
105146397622STakashi Iwai if (copy_from_user(ev, buf, len)) {
10521da177e4SLinus Torvalds err = -EFAULT;
10531da177e4SLinus Torvalds break;
10541da177e4SLinus Torvalds }
105546397622STakashi Iwai /* read in the rest bytes for UMP events */
105646397622STakashi Iwai if (snd_seq_ev_is_ump(ev)) {
105746397622STakashi Iwai if (count < sizeof(struct snd_seq_ump_event))
105846397622STakashi Iwai break;
105946397622STakashi Iwai if (copy_from_user((char *)ev + len, buf + len,
106046397622STakashi Iwai sizeof(struct snd_seq_ump_event) - len)) {
106146397622STakashi Iwai err = -EFAULT;
106246397622STakashi Iwai break;
106346397622STakashi Iwai }
106446397622STakashi Iwai len = sizeof(struct snd_seq_ump_event);
106546397622STakashi Iwai }
106646397622STakashi Iwai
106746397622STakashi Iwai ev->source.client = client->number; /* fill in client number */
10681da177e4SLinus Torvalds /* Check for extension data length */
106946397622STakashi Iwai if (check_event_type_and_length(ev)) {
107046397622STakashi Iwai err = -EINVAL;
107146397622STakashi Iwai break;
107246397622STakashi Iwai }
107346397622STakashi Iwai
107446397622STakashi Iwai if (!event_is_compatible(client, ev)) {
10751da177e4SLinus Torvalds err = -EINVAL;
10761da177e4SLinus Torvalds break;
10771da177e4SLinus Torvalds }
10781da177e4SLinus Torvalds
10791da177e4SLinus Torvalds /* check for special events */
108046397622STakashi Iwai if (!snd_seq_ev_is_ump(ev)) {
108146397622STakashi Iwai if (ev->type == SNDRV_SEQ_EVENT_NONE)
10821da177e4SLinus Torvalds goto __skip_event;
108346397622STakashi Iwai else if (snd_seq_ev_is_reserved(ev)) {
10841da177e4SLinus Torvalds err = -EINVAL;
10851da177e4SLinus Torvalds break;
10861da177e4SLinus Torvalds }
108746397622STakashi Iwai }
10881da177e4SLinus Torvalds
108946397622STakashi Iwai if (snd_seq_ev_is_variable(ev)) {
109046397622STakashi Iwai int extlen = ev->data.ext.len & ~SNDRV_SEQ_EXT_MASK;
10911da177e4SLinus Torvalds if ((size_t)(extlen + len) > count) {
10921da177e4SLinus Torvalds /* back out, will get an error this time or next */
10931da177e4SLinus Torvalds err = -EINVAL;
10941da177e4SLinus Torvalds break;
10951da177e4SLinus Torvalds }
10961da177e4SLinus Torvalds /* set user space pointer */
109746397622STakashi Iwai ev->data.ext.len = extlen | SNDRV_SEQ_EXT_USRPTR;
109846397622STakashi Iwai ev->data.ext.ptr = (char __force *)buf + len;
10991da177e4SLinus Torvalds len += extlen; /* increment data length */
11001da177e4SLinus Torvalds } else {
11011da177e4SLinus Torvalds #ifdef CONFIG_COMPAT
110246397622STakashi Iwai if (client->convert32 && snd_seq_ev_is_varusr(ev))
110346397622STakashi Iwai ev->data.ext.ptr =
110446397622STakashi Iwai (void __force *)compat_ptr(ev->data.raw32.d[1]);
11051da177e4SLinus Torvalds #endif
11061da177e4SLinus Torvalds }
11071da177e4SLinus Torvalds
11081da177e4SLinus Torvalds /* ok, enqueue it */
110946397622STakashi Iwai err = snd_seq_client_enqueue_event(client, ev, file,
11101da177e4SLinus Torvalds !(file->f_flags & O_NONBLOCK),
11117bd80091STakashi Iwai 0, 0, &client->ioctl_mutex);
11121da177e4SLinus Torvalds if (err < 0)
11131da177e4SLinus Torvalds break;
1114ede34f39STakashi Iwai handled++;
11151da177e4SLinus Torvalds
11161da177e4SLinus Torvalds __skip_event:
11171da177e4SLinus Torvalds /* Update pointers and counts */
11181da177e4SLinus Torvalds count -= len;
11191da177e4SLinus Torvalds buf += len;
11201da177e4SLinus Torvalds written += len;
1121ede34f39STakashi Iwai
1122ede34f39STakashi Iwai /* let's have a coffee break if too many events are queued */
1123ede34f39STakashi Iwai if (++handled >= 200) {
1124ede34f39STakashi Iwai mutex_unlock(&client->ioctl_mutex);
1125ede34f39STakashi Iwai goto repeat;
1126ede34f39STakashi Iwai }
11271da177e4SLinus Torvalds }
11281da177e4SLinus Torvalds
11297bd80091STakashi Iwai out:
11307bd80091STakashi Iwai mutex_unlock(&client->ioctl_mutex);
11311da177e4SLinus Torvalds return written ? written : err;
11321da177e4SLinus Torvalds }
11331da177e4SLinus Torvalds
11341da177e4SLinus Torvalds
11351da177e4SLinus Torvalds /*
11361da177e4SLinus Torvalds * handle polling
11371da177e4SLinus Torvalds */
snd_seq_poll(struct file * file,poll_table * wait)1138680ef72aSAl Viro static __poll_t snd_seq_poll(struct file *file, poll_table * wait)
11391da177e4SLinus Torvalds {
1140c7e0b5bfSTakashi Iwai struct snd_seq_client *client = file->private_data;
1141680ef72aSAl Viro __poll_t mask = 0;
11421da177e4SLinus Torvalds
11431da177e4SLinus Torvalds /* check client structures are in place */
11447eaa943cSTakashi Iwai if (snd_BUG_ON(!client))
1145a49a71f6STakashi Iwai return EPOLLERR;
11461da177e4SLinus Torvalds
11471da177e4SLinus Torvalds if ((snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT) &&
11481da177e4SLinus Torvalds client->data.user.fifo) {
11491da177e4SLinus Torvalds
11501da177e4SLinus Torvalds /* check if data is available in the outqueue */
11511da177e4SLinus Torvalds if (snd_seq_fifo_poll_wait(client->data.user.fifo, file, wait))
1152a9a08845SLinus Torvalds mask |= EPOLLIN | EPOLLRDNORM;
11531da177e4SLinus Torvalds }
11541da177e4SLinus Torvalds
11551da177e4SLinus Torvalds if (snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT) {
11561da177e4SLinus Torvalds
11571da177e4SLinus Torvalds /* check if data is available in the pool */
11581da177e4SLinus Torvalds if (!snd_seq_write_pool_allocated(client) ||
11591da177e4SLinus Torvalds snd_seq_pool_poll_wait(client->pool, file, wait))
1160a9a08845SLinus Torvalds mask |= EPOLLOUT | EPOLLWRNORM;
11611da177e4SLinus Torvalds }
11621da177e4SLinus Torvalds
11631da177e4SLinus Torvalds return mask;
11641da177e4SLinus Torvalds }
11651da177e4SLinus Torvalds
11661da177e4SLinus Torvalds
11671da177e4SLinus Torvalds /*-----------------------------------------------------*/
11681da177e4SLinus Torvalds
snd_seq_ioctl_pversion(struct snd_seq_client * client,void * arg)116904a56dd8STakashi Sakamoto static int snd_seq_ioctl_pversion(struct snd_seq_client *client, void *arg)
117004a56dd8STakashi Sakamoto {
117104a56dd8STakashi Sakamoto int *pversion = arg;
117204a56dd8STakashi Sakamoto
117304a56dd8STakashi Sakamoto *pversion = SNDRV_SEQ_VERSION;
117404a56dd8STakashi Sakamoto return 0;
117504a56dd8STakashi Sakamoto }
117604a56dd8STakashi Sakamoto
snd_seq_ioctl_user_pversion(struct snd_seq_client * client,void * arg)1177afb72505STakashi Iwai static int snd_seq_ioctl_user_pversion(struct snd_seq_client *client, void *arg)
1178afb72505STakashi Iwai {
1179afb72505STakashi Iwai client->user_pversion = *(unsigned int *)arg;
1180afb72505STakashi Iwai return 0;
1181afb72505STakashi Iwai }
1182afb72505STakashi Iwai
snd_seq_ioctl_client_id(struct snd_seq_client * client,void * arg)118304a56dd8STakashi Sakamoto static int snd_seq_ioctl_client_id(struct snd_seq_client *client, void *arg)
118404a56dd8STakashi Sakamoto {
118504a56dd8STakashi Sakamoto int *client_id = arg;
118604a56dd8STakashi Sakamoto
118704a56dd8STakashi Sakamoto *client_id = client->number;
118804a56dd8STakashi Sakamoto return 0;
118904a56dd8STakashi Sakamoto }
11901da177e4SLinus Torvalds
11911da177e4SLinus Torvalds /* SYSTEM_INFO ioctl() */
snd_seq_ioctl_system_info(struct snd_seq_client * client,void * arg)119204a56dd8STakashi Sakamoto static int snd_seq_ioctl_system_info(struct snd_seq_client *client, void *arg)
11931da177e4SLinus Torvalds {
119404a56dd8STakashi Sakamoto struct snd_seq_system_info *info = arg;
11951da177e4SLinus Torvalds
119604a56dd8STakashi Sakamoto memset(info, 0, sizeof(*info));
11971da177e4SLinus Torvalds /* fill the info fields */
119804a56dd8STakashi Sakamoto info->queues = SNDRV_SEQ_MAX_QUEUES;
119904a56dd8STakashi Sakamoto info->clients = SNDRV_SEQ_MAX_CLIENTS;
120004a56dd8STakashi Sakamoto info->ports = SNDRV_SEQ_MAX_PORTS;
120104a56dd8STakashi Sakamoto info->channels = 256; /* fixed limit */
120204a56dd8STakashi Sakamoto info->cur_clients = client_usage.cur;
120304a56dd8STakashi Sakamoto info->cur_queues = snd_seq_queue_get_cur_queues();
12041da177e4SLinus Torvalds
12051da177e4SLinus Torvalds return 0;
12061da177e4SLinus Torvalds }
12071da177e4SLinus Torvalds
12081da177e4SLinus Torvalds
12091da177e4SLinus Torvalds /* RUNNING_MODE ioctl() */
snd_seq_ioctl_running_mode(struct snd_seq_client * client,void * arg)121004a56dd8STakashi Sakamoto static int snd_seq_ioctl_running_mode(struct snd_seq_client *client, void *arg)
12111da177e4SLinus Torvalds {
121204a56dd8STakashi Sakamoto struct snd_seq_running_info *info = arg;
1213c7e0b5bfSTakashi Iwai struct snd_seq_client *cptr;
12141da177e4SLinus Torvalds int err = 0;
12151da177e4SLinus Torvalds
12161da177e4SLinus Torvalds /* requested client number */
1217*5e1b3bf7STakashi Iwai cptr = client_load_and_use_ptr(info->client);
12181da177e4SLinus Torvalds if (cptr == NULL)
12191da177e4SLinus Torvalds return -ENOENT; /* don't change !!! */
12201da177e4SLinus Torvalds
12211da177e4SLinus Torvalds #ifdef SNDRV_BIG_ENDIAN
122204a56dd8STakashi Sakamoto if (!info->big_endian) {
12231da177e4SLinus Torvalds err = -EINVAL;
12241da177e4SLinus Torvalds goto __err;
12251da177e4SLinus Torvalds }
12261da177e4SLinus Torvalds #else
122704a56dd8STakashi Sakamoto if (info->big_endian) {
12281da177e4SLinus Torvalds err = -EINVAL;
12291da177e4SLinus Torvalds goto __err;
12301da177e4SLinus Torvalds }
12311da177e4SLinus Torvalds
12321da177e4SLinus Torvalds #endif
123304a56dd8STakashi Sakamoto if (info->cpu_mode > sizeof(long)) {
12341da177e4SLinus Torvalds err = -EINVAL;
12351da177e4SLinus Torvalds goto __err;
12361da177e4SLinus Torvalds }
123704a56dd8STakashi Sakamoto cptr->convert32 = (info->cpu_mode < sizeof(long));
12381da177e4SLinus Torvalds __err:
12391da177e4SLinus Torvalds snd_seq_client_unlock(cptr);
12401da177e4SLinus Torvalds return err;
12411da177e4SLinus Torvalds }
12421da177e4SLinus Torvalds
12431da177e4SLinus Torvalds /* CLIENT_INFO ioctl() */
get_client_info(struct snd_seq_client * cptr,struct snd_seq_client_info * info)1244c7e0b5bfSTakashi Iwai static void get_client_info(struct snd_seq_client *cptr,
1245c7e0b5bfSTakashi Iwai struct snd_seq_client_info *info)
12461da177e4SLinus Torvalds {
12471da177e4SLinus Torvalds info->client = cptr->number;
12481da177e4SLinus Torvalds
12491da177e4SLinus Torvalds /* fill the info fields */
12501da177e4SLinus Torvalds info->type = cptr->type;
12511da177e4SLinus Torvalds strcpy(info->name, cptr->name);
12521da177e4SLinus Torvalds info->filter = cptr->filter;
12531da177e4SLinus Torvalds info->event_lost = cptr->event_lost;
12541da177e4SLinus Torvalds memcpy(info->event_filter, cptr->event_filter, 32);
1255d2b70607STakashi Iwai info->group_filter = cptr->group_filter;
12561da177e4SLinus Torvalds info->num_ports = cptr->num_ports;
1257a1ce94d0SMartin Koegler
1258a1ce94d0SMartin Koegler if (cptr->type == USER_CLIENT)
1259a1ce94d0SMartin Koegler info->pid = pid_vnr(cptr->data.user.owner);
1260a1ce94d0SMartin Koegler else
1261a1ce94d0SMartin Koegler info->pid = -1;
1262a1ce94d0SMartin Koegler
1263a1ce94d0SMartin Koegler if (cptr->type == KERNEL_CLIENT)
1264a1ce94d0SMartin Koegler info->card = cptr->data.kernel.card ? cptr->data.kernel.card->number : -1;
1265a1ce94d0SMartin Koegler else
1266a1ce94d0SMartin Koegler info->card = -1;
1267a1ce94d0SMartin Koegler
126846397622STakashi Iwai info->midi_version = cptr->midi_version;
12691da177e4SLinus Torvalds memset(info->reserved, 0, sizeof(info->reserved));
12701da177e4SLinus Torvalds }
12711da177e4SLinus Torvalds
snd_seq_ioctl_get_client_info(struct snd_seq_client * client,void * arg)1272c7e0b5bfSTakashi Iwai static int snd_seq_ioctl_get_client_info(struct snd_seq_client *client,
127304a56dd8STakashi Sakamoto void *arg)
12741da177e4SLinus Torvalds {
127504a56dd8STakashi Sakamoto struct snd_seq_client_info *client_info = arg;
1276c7e0b5bfSTakashi Iwai struct snd_seq_client *cptr;
12771da177e4SLinus Torvalds
12781da177e4SLinus Torvalds /* requested client number */
1279*5e1b3bf7STakashi Iwai cptr = client_load_and_use_ptr(client_info->client);
12801da177e4SLinus Torvalds if (cptr == NULL)
12811da177e4SLinus Torvalds return -ENOENT; /* don't change !!! */
12821da177e4SLinus Torvalds
128304a56dd8STakashi Sakamoto get_client_info(cptr, client_info);
12841da177e4SLinus Torvalds snd_seq_client_unlock(cptr);
12851da177e4SLinus Torvalds
12861da177e4SLinus Torvalds return 0;
12871da177e4SLinus Torvalds }
12881da177e4SLinus Torvalds
12891da177e4SLinus Torvalds
12901da177e4SLinus Torvalds /* CLIENT_INFO ioctl() */
snd_seq_ioctl_set_client_info(struct snd_seq_client * client,void * arg)1291c7e0b5bfSTakashi Iwai static int snd_seq_ioctl_set_client_info(struct snd_seq_client *client,
129204a56dd8STakashi Sakamoto void *arg)
12931da177e4SLinus Torvalds {
129404a56dd8STakashi Sakamoto struct snd_seq_client_info *client_info = arg;
12951da177e4SLinus Torvalds
12961da177e4SLinus Torvalds /* it is not allowed to set the info fields for an another client */
129704a56dd8STakashi Sakamoto if (client->number != client_info->client)
12981da177e4SLinus Torvalds return -EPERM;
12991da177e4SLinus Torvalds /* also client type must be set now */
130004a56dd8STakashi Sakamoto if (client->type != client_info->type)
13011da177e4SLinus Torvalds return -EINVAL;
13021da177e4SLinus Torvalds
1303396964d4STakashi Iwai if (client->user_pversion >= SNDRV_PROTOCOL_VERSION(1, 0, 3)) {
130446397622STakashi Iwai /* check validity of midi_version field */
1305396964d4STakashi Iwai if (client_info->midi_version > SNDRV_SEQ_CLIENT_UMP_MIDI_2_0)
130646397622STakashi Iwai return -EINVAL;
130746397622STakashi Iwai
1308396964d4STakashi Iwai /* check if UMP is supported in kernel */
1309396964d4STakashi Iwai if (!IS_ENABLED(CONFIG_SND_SEQ_UMP) &&
1310396964d4STakashi Iwai client_info->midi_version > 0)
1311396964d4STakashi Iwai return -EINVAL;
1312396964d4STakashi Iwai }
1313396964d4STakashi Iwai
13141da177e4SLinus Torvalds /* fill the info fields */
131504a56dd8STakashi Sakamoto if (client_info->name[0])
1316212ac181SZubin Mithra strscpy(client->name, client_info->name, sizeof(client->name));
13171da177e4SLinus Torvalds
131804a56dd8STakashi Sakamoto client->filter = client_info->filter;
131904a56dd8STakashi Sakamoto client->event_lost = client_info->event_lost;
132046397622STakashi Iwai if (client->user_pversion >= SNDRV_PROTOCOL_VERSION(1, 0, 3))
132146397622STakashi Iwai client->midi_version = client_info->midi_version;
132204a56dd8STakashi Sakamoto memcpy(client->event_filter, client_info->event_filter, 32);
1323d2b70607STakashi Iwai client->group_filter = client_info->group_filter;
13241da177e4SLinus Torvalds return 0;
13251da177e4SLinus Torvalds }
13261da177e4SLinus Torvalds
13271da177e4SLinus Torvalds
13281da177e4SLinus Torvalds /*
13291da177e4SLinus Torvalds * CREATE PORT ioctl()
13301da177e4SLinus Torvalds */
snd_seq_ioctl_create_port(struct snd_seq_client * client,void * arg)133104a56dd8STakashi Sakamoto static int snd_seq_ioctl_create_port(struct snd_seq_client *client, void *arg)
13321da177e4SLinus Torvalds {
133304a56dd8STakashi Sakamoto struct snd_seq_port_info *info = arg;
1334c7e0b5bfSTakashi Iwai struct snd_seq_client_port *port;
1335c7e0b5bfSTakashi Iwai struct snd_seq_port_callback *callback;
13367c3f0d3dSTakashi Iwai int port_idx, err;
13371da177e4SLinus Torvalds
13381da177e4SLinus Torvalds /* it is not allowed to create the port for an another client */
133904a56dd8STakashi Sakamoto if (info->addr.client != client->number)
13401da177e4SLinus Torvalds return -EPERM;
13414f92eb79STakashi Iwai if (client->type == USER_CLIENT && info->kernel)
13424f92eb79STakashi Iwai return -EINVAL;
1343177ccf81STakashi Iwai if ((info->capability & SNDRV_SEQ_PORT_CAP_UMP_ENDPOINT) &&
1344177ccf81STakashi Iwai client->ump_endpoint_port >= 0)
1345177ccf81STakashi Iwai return -EBUSY;
13461da177e4SLinus Torvalds
13477c3f0d3dSTakashi Iwai if (info->flags & SNDRV_SEQ_PORT_FLG_GIVEN_PORT)
13487c3f0d3dSTakashi Iwai port_idx = info->addr.port;
13497c3f0d3dSTakashi Iwai else
13507c3f0d3dSTakashi Iwai port_idx = -1;
135113599053STakashi Iwai if (port_idx >= SNDRV_SEQ_ADDRESS_UNKNOWN)
135213599053STakashi Iwai return -EINVAL;
13537c3f0d3dSTakashi Iwai err = snd_seq_create_port(client, port_idx, &port);
13547c3f0d3dSTakashi Iwai if (err < 0)
13557c3f0d3dSTakashi Iwai return err;
13561da177e4SLinus Torvalds
13571da177e4SLinus Torvalds if (client->type == KERNEL_CLIENT) {
1358f9a6bb84STakashi Iwai callback = info->kernel;
1359f9a6bb84STakashi Iwai if (callback) {
13601da177e4SLinus Torvalds if (callback->owner)
13611da177e4SLinus Torvalds port->owner = callback->owner;
13621da177e4SLinus Torvalds port->private_data = callback->private_data;
13631da177e4SLinus Torvalds port->private_free = callback->private_free;
13641da177e4SLinus Torvalds port->event_input = callback->event_input;
13651da177e4SLinus Torvalds port->c_src.open = callback->subscribe;
13661da177e4SLinus Torvalds port->c_src.close = callback->unsubscribe;
13671da177e4SLinus Torvalds port->c_dest.open = callback->use;
13681da177e4SLinus Torvalds port->c_dest.close = callback->unuse;
13691da177e4SLinus Torvalds }
13701da177e4SLinus Torvalds }
13711da177e4SLinus Torvalds
137204a56dd8STakashi Sakamoto info->addr = port->addr;
13731da177e4SLinus Torvalds
137404a56dd8STakashi Sakamoto snd_seq_set_port_info(port, info);
1375177ccf81STakashi Iwai if (info->capability & SNDRV_SEQ_PORT_CAP_UMP_ENDPOINT)
1376177ccf81STakashi Iwai client->ump_endpoint_port = port->addr.port;
13771da177e4SLinus Torvalds snd_seq_system_client_ev_port_start(port->addr.client, port->addr.port);
137871105998STakashi Iwai snd_seq_port_unlock(port);
13791da177e4SLinus Torvalds
13801da177e4SLinus Torvalds return 0;
13811da177e4SLinus Torvalds }
13821da177e4SLinus Torvalds
13831da177e4SLinus Torvalds /*
13841da177e4SLinus Torvalds * DELETE PORT ioctl()
13851da177e4SLinus Torvalds */
snd_seq_ioctl_delete_port(struct snd_seq_client * client,void * arg)138604a56dd8STakashi Sakamoto static int snd_seq_ioctl_delete_port(struct snd_seq_client *client, void *arg)
13871da177e4SLinus Torvalds {
138804a56dd8STakashi Sakamoto struct snd_seq_port_info *info = arg;
13891da177e4SLinus Torvalds int err;
13901da177e4SLinus Torvalds
13911da177e4SLinus Torvalds /* it is not allowed to remove the port for an another client */
139204a56dd8STakashi Sakamoto if (info->addr.client != client->number)
13931da177e4SLinus Torvalds return -EPERM;
13941da177e4SLinus Torvalds
139504a56dd8STakashi Sakamoto err = snd_seq_delete_port(client, info->addr.port);
1396177ccf81STakashi Iwai if (err >= 0) {
1397177ccf81STakashi Iwai if (client->ump_endpoint_port == info->addr.port)
1398177ccf81STakashi Iwai client->ump_endpoint_port = -1;
139904a56dd8STakashi Sakamoto snd_seq_system_client_ev_port_exit(client->number, info->addr.port);
1400177ccf81STakashi Iwai }
14011da177e4SLinus Torvalds return err;
14021da177e4SLinus Torvalds }
14031da177e4SLinus Torvalds
14041da177e4SLinus Torvalds
14051da177e4SLinus Torvalds /*
14061da177e4SLinus Torvalds * GET_PORT_INFO ioctl() (on any client)
14071da177e4SLinus Torvalds */
snd_seq_ioctl_get_port_info(struct snd_seq_client * client,void * arg)140804a56dd8STakashi Sakamoto static int snd_seq_ioctl_get_port_info(struct snd_seq_client *client, void *arg)
14091da177e4SLinus Torvalds {
141004a56dd8STakashi Sakamoto struct snd_seq_port_info *info = arg;
1411c7e0b5bfSTakashi Iwai struct snd_seq_client *cptr;
1412c7e0b5bfSTakashi Iwai struct snd_seq_client_port *port;
14131da177e4SLinus Torvalds
1414*5e1b3bf7STakashi Iwai cptr = client_load_and_use_ptr(info->addr.client);
14151da177e4SLinus Torvalds if (cptr == NULL)
14161da177e4SLinus Torvalds return -ENXIO;
14171da177e4SLinus Torvalds
141804a56dd8STakashi Sakamoto port = snd_seq_port_use_ptr(cptr, info->addr.port);
14191da177e4SLinus Torvalds if (port == NULL) {
14201da177e4SLinus Torvalds snd_seq_client_unlock(cptr);
14211da177e4SLinus Torvalds return -ENOENT; /* don't change */
14221da177e4SLinus Torvalds }
14231da177e4SLinus Torvalds
14241da177e4SLinus Torvalds /* get port info */
142504a56dd8STakashi Sakamoto snd_seq_get_port_info(port, info);
14261da177e4SLinus Torvalds snd_seq_port_unlock(port);
14271da177e4SLinus Torvalds snd_seq_client_unlock(cptr);
14281da177e4SLinus Torvalds
14291da177e4SLinus Torvalds return 0;
14301da177e4SLinus Torvalds }
14311da177e4SLinus Torvalds
14321da177e4SLinus Torvalds
14331da177e4SLinus Torvalds /*
14341da177e4SLinus Torvalds * SET_PORT_INFO ioctl() (only ports on this/own client)
14351da177e4SLinus Torvalds */
snd_seq_ioctl_set_port_info(struct snd_seq_client * client,void * arg)143604a56dd8STakashi Sakamoto static int snd_seq_ioctl_set_port_info(struct snd_seq_client *client, void *arg)
14371da177e4SLinus Torvalds {
143804a56dd8STakashi Sakamoto struct snd_seq_port_info *info = arg;
1439c7e0b5bfSTakashi Iwai struct snd_seq_client_port *port;
14401da177e4SLinus Torvalds
144104a56dd8STakashi Sakamoto if (info->addr.client != client->number) /* only set our own ports ! */
14421da177e4SLinus Torvalds return -EPERM;
144304a56dd8STakashi Sakamoto port = snd_seq_port_use_ptr(client, info->addr.port);
14441da177e4SLinus Torvalds if (port) {
144504a56dd8STakashi Sakamoto snd_seq_set_port_info(port, info);
14461da177e4SLinus Torvalds snd_seq_port_unlock(port);
14471da177e4SLinus Torvalds }
14481da177e4SLinus Torvalds return 0;
14491da177e4SLinus Torvalds }
14501da177e4SLinus Torvalds
14511da177e4SLinus Torvalds
14521da177e4SLinus Torvalds /*
14531da177e4SLinus Torvalds * port subscription (connection)
14541da177e4SLinus Torvalds */
14551da177e4SLinus Torvalds #define PERM_RD (SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ)
14561da177e4SLinus Torvalds #define PERM_WR (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE)
14571da177e4SLinus Torvalds
check_subscription_permission(struct snd_seq_client * client,struct snd_seq_client_port * sport,struct snd_seq_client_port * dport,struct snd_seq_port_subscribe * subs)1458c7e0b5bfSTakashi Iwai static int check_subscription_permission(struct snd_seq_client *client,
1459c7e0b5bfSTakashi Iwai struct snd_seq_client_port *sport,
1460c7e0b5bfSTakashi Iwai struct snd_seq_client_port *dport,
1461c7e0b5bfSTakashi Iwai struct snd_seq_port_subscribe *subs)
14621da177e4SLinus Torvalds {
14631da177e4SLinus Torvalds if (client->number != subs->sender.client &&
14641da177e4SLinus Torvalds client->number != subs->dest.client) {
14651da177e4SLinus Torvalds /* connection by third client - check export permission */
14661da177e4SLinus Torvalds if (check_port_perm(sport, SNDRV_SEQ_PORT_CAP_NO_EXPORT))
14671da177e4SLinus Torvalds return -EPERM;
14681da177e4SLinus Torvalds if (check_port_perm(dport, SNDRV_SEQ_PORT_CAP_NO_EXPORT))
14691da177e4SLinus Torvalds return -EPERM;
14701da177e4SLinus Torvalds }
14711da177e4SLinus Torvalds
14721da177e4SLinus Torvalds /* check read permission */
14731da177e4SLinus Torvalds /* if sender or receiver is the subscribing client itself,
14741da177e4SLinus Torvalds * no permission check is necessary
14751da177e4SLinus Torvalds */
14761da177e4SLinus Torvalds if (client->number != subs->sender.client) {
14771da177e4SLinus Torvalds if (! check_port_perm(sport, PERM_RD))
14781da177e4SLinus Torvalds return -EPERM;
14791da177e4SLinus Torvalds }
14801da177e4SLinus Torvalds /* check write permission */
14811da177e4SLinus Torvalds if (client->number != subs->dest.client) {
14821da177e4SLinus Torvalds if (! check_port_perm(dport, PERM_WR))
14831da177e4SLinus Torvalds return -EPERM;
14841da177e4SLinus Torvalds }
14851da177e4SLinus Torvalds return 0;
14861da177e4SLinus Torvalds }
14871da177e4SLinus Torvalds
14881da177e4SLinus Torvalds /*
14891da177e4SLinus Torvalds * send an subscription notify event to user client:
14901da177e4SLinus Torvalds * client must be user client.
14911da177e4SLinus Torvalds */
snd_seq_client_notify_subscription(int client,int port,struct snd_seq_port_subscribe * info,int evtype)14921da177e4SLinus Torvalds int snd_seq_client_notify_subscription(int client, int port,
1493c7e0b5bfSTakashi Iwai struct snd_seq_port_subscribe *info,
1494c7e0b5bfSTakashi Iwai int evtype)
14951da177e4SLinus Torvalds {
1496c7e0b5bfSTakashi Iwai struct snd_seq_event event;
14971da177e4SLinus Torvalds
14981da177e4SLinus Torvalds memset(&event, 0, sizeof(event));
14991da177e4SLinus Torvalds event.type = evtype;
15001da177e4SLinus Torvalds event.data.connect.dest = info->dest;
15011da177e4SLinus Torvalds event.data.connect.sender = info->sender;
15021da177e4SLinus Torvalds
15031da177e4SLinus Torvalds return snd_seq_system_notify(client, port, &event); /* non-atomic */
15041da177e4SLinus Torvalds }
15051da177e4SLinus Torvalds
15061da177e4SLinus Torvalds
15071da177e4SLinus Torvalds /*
15081da177e4SLinus Torvalds * add to port's subscription list IOCTL interface
15091da177e4SLinus Torvalds */
snd_seq_ioctl_subscribe_port(struct snd_seq_client * client,void * arg)1510c7e0b5bfSTakashi Iwai static int snd_seq_ioctl_subscribe_port(struct snd_seq_client *client,
151104a56dd8STakashi Sakamoto void *arg)
15121da177e4SLinus Torvalds {
151304a56dd8STakashi Sakamoto struct snd_seq_port_subscribe *subs = arg;
15141da177e4SLinus Torvalds int result = -EINVAL;
1515c7e0b5bfSTakashi Iwai struct snd_seq_client *receiver = NULL, *sender = NULL;
1516c7e0b5bfSTakashi Iwai struct snd_seq_client_port *sport = NULL, *dport = NULL;
15171da177e4SLinus Torvalds
1518*5e1b3bf7STakashi Iwai receiver = client_load_and_use_ptr(subs->dest.client);
1519f9a6bb84STakashi Iwai if (!receiver)
15201da177e4SLinus Torvalds goto __end;
1521*5e1b3bf7STakashi Iwai sender = client_load_and_use_ptr(subs->sender.client);
1522f9a6bb84STakashi Iwai if (!sender)
15231da177e4SLinus Torvalds goto __end;
1524f9a6bb84STakashi Iwai sport = snd_seq_port_use_ptr(sender, subs->sender.port);
1525f9a6bb84STakashi Iwai if (!sport)
15261da177e4SLinus Torvalds goto __end;
1527f9a6bb84STakashi Iwai dport = snd_seq_port_use_ptr(receiver, subs->dest.port);
1528f9a6bb84STakashi Iwai if (!dport)
15291da177e4SLinus Torvalds goto __end;
15301da177e4SLinus Torvalds
153104a56dd8STakashi Sakamoto result = check_subscription_permission(client, sport, dport, subs);
15321da177e4SLinus Torvalds if (result < 0)
15331da177e4SLinus Torvalds goto __end;
15341da177e4SLinus Torvalds
15351da177e4SLinus Torvalds /* connect them */
153604a56dd8STakashi Sakamoto result = snd_seq_port_connect(client, sender, sport, receiver, dport, subs);
15371da177e4SLinus Torvalds if (! result) /* broadcast announce */
15381da177e4SLinus Torvalds snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, 0,
153904a56dd8STakashi Sakamoto subs, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED);
15401da177e4SLinus Torvalds __end:
15411da177e4SLinus Torvalds if (sport)
15421da177e4SLinus Torvalds snd_seq_port_unlock(sport);
15431da177e4SLinus Torvalds if (dport)
15441da177e4SLinus Torvalds snd_seq_port_unlock(dport);
15451da177e4SLinus Torvalds if (sender)
15461da177e4SLinus Torvalds snd_seq_client_unlock(sender);
15471da177e4SLinus Torvalds if (receiver)
15481da177e4SLinus Torvalds snd_seq_client_unlock(receiver);
15491da177e4SLinus Torvalds return result;
15501da177e4SLinus Torvalds }
15511da177e4SLinus Torvalds
15521da177e4SLinus Torvalds
15531da177e4SLinus Torvalds /*
15541da177e4SLinus Torvalds * remove from port's subscription list
15551da177e4SLinus Torvalds */
snd_seq_ioctl_unsubscribe_port(struct snd_seq_client * client,void * arg)1556c7e0b5bfSTakashi Iwai static int snd_seq_ioctl_unsubscribe_port(struct snd_seq_client *client,
155704a56dd8STakashi Sakamoto void *arg)
15581da177e4SLinus Torvalds {
155904a56dd8STakashi Sakamoto struct snd_seq_port_subscribe *subs = arg;
15601da177e4SLinus Torvalds int result = -ENXIO;
1561c7e0b5bfSTakashi Iwai struct snd_seq_client *receiver = NULL, *sender = NULL;
1562c7e0b5bfSTakashi Iwai struct snd_seq_client_port *sport = NULL, *dport = NULL;
15631da177e4SLinus Torvalds
1564f9a6bb84STakashi Iwai receiver = snd_seq_client_use_ptr(subs->dest.client);
1565f9a6bb84STakashi Iwai if (!receiver)
15661da177e4SLinus Torvalds goto __end;
1567f9a6bb84STakashi Iwai sender = snd_seq_client_use_ptr(subs->sender.client);
1568f9a6bb84STakashi Iwai if (!sender)
15691da177e4SLinus Torvalds goto __end;
1570f9a6bb84STakashi Iwai sport = snd_seq_port_use_ptr(sender, subs->sender.port);
1571f9a6bb84STakashi Iwai if (!sport)
15721da177e4SLinus Torvalds goto __end;
1573f9a6bb84STakashi Iwai dport = snd_seq_port_use_ptr(receiver, subs->dest.port);
1574f9a6bb84STakashi Iwai if (!dport)
15751da177e4SLinus Torvalds goto __end;
15761da177e4SLinus Torvalds
157704a56dd8STakashi Sakamoto result = check_subscription_permission(client, sport, dport, subs);
15781da177e4SLinus Torvalds if (result < 0)
15791da177e4SLinus Torvalds goto __end;
15801da177e4SLinus Torvalds
158104a56dd8STakashi Sakamoto result = snd_seq_port_disconnect(client, sender, sport, receiver, dport, subs);
15821da177e4SLinus Torvalds if (! result) /* broadcast announce */
15831da177e4SLinus Torvalds snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, 0,
158404a56dd8STakashi Sakamoto subs, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED);
15851da177e4SLinus Torvalds __end:
15861da177e4SLinus Torvalds if (sport)
15871da177e4SLinus Torvalds snd_seq_port_unlock(sport);
15881da177e4SLinus Torvalds if (dport)
15891da177e4SLinus Torvalds snd_seq_port_unlock(dport);
15901da177e4SLinus Torvalds if (sender)
15911da177e4SLinus Torvalds snd_seq_client_unlock(sender);
15921da177e4SLinus Torvalds if (receiver)
15931da177e4SLinus Torvalds snd_seq_client_unlock(receiver);
15941da177e4SLinus Torvalds return result;
15951da177e4SLinus Torvalds }
15961da177e4SLinus Torvalds
15971da177e4SLinus Torvalds
15981da177e4SLinus Torvalds /* CREATE_QUEUE ioctl() */
snd_seq_ioctl_create_queue(struct snd_seq_client * client,void * arg)159904a56dd8STakashi Sakamoto static int snd_seq_ioctl_create_queue(struct snd_seq_client *client, void *arg)
16001da177e4SLinus Torvalds {
160104a56dd8STakashi Sakamoto struct snd_seq_queue_info *info = arg;
1602c7e0b5bfSTakashi Iwai struct snd_seq_queue *q;
16031da177e4SLinus Torvalds
16047e1d90f6SDaniel Mentz q = snd_seq_queue_alloc(client->number, info->locked, info->flags);
16057e1d90f6SDaniel Mentz if (IS_ERR(q))
16067e1d90f6SDaniel Mentz return PTR_ERR(q);
16071da177e4SLinus Torvalds
160804a56dd8STakashi Sakamoto info->queue = q->queue;
160904a56dd8STakashi Sakamoto info->locked = q->locked;
161004a56dd8STakashi Sakamoto info->owner = q->owner;
16111da177e4SLinus Torvalds
16121da177e4SLinus Torvalds /* set queue name */
161304a56dd8STakashi Sakamoto if (!info->name[0])
161404a56dd8STakashi Sakamoto snprintf(info->name, sizeof(info->name), "Queue-%d", q->queue);
1615212ac181SZubin Mithra strscpy(q->name, info->name, sizeof(q->name));
16167e1d90f6SDaniel Mentz snd_use_lock_free(&q->use_lock);
16171da177e4SLinus Torvalds
16181da177e4SLinus Torvalds return 0;
16191da177e4SLinus Torvalds }
16201da177e4SLinus Torvalds
16211da177e4SLinus Torvalds /* DELETE_QUEUE ioctl() */
snd_seq_ioctl_delete_queue(struct snd_seq_client * client,void * arg)162204a56dd8STakashi Sakamoto static int snd_seq_ioctl_delete_queue(struct snd_seq_client *client, void *arg)
16231da177e4SLinus Torvalds {
162404a56dd8STakashi Sakamoto struct snd_seq_queue_info *info = arg;
16251da177e4SLinus Torvalds
162604a56dd8STakashi Sakamoto return snd_seq_queue_delete(client->number, info->queue);
16271da177e4SLinus Torvalds }
16281da177e4SLinus Torvalds
16291da177e4SLinus Torvalds /* GET_QUEUE_INFO ioctl() */
snd_seq_ioctl_get_queue_info(struct snd_seq_client * client,void * arg)1630c7e0b5bfSTakashi Iwai static int snd_seq_ioctl_get_queue_info(struct snd_seq_client *client,
163104a56dd8STakashi Sakamoto void *arg)
16321da177e4SLinus Torvalds {
163304a56dd8STakashi Sakamoto struct snd_seq_queue_info *info = arg;
1634c7e0b5bfSTakashi Iwai struct snd_seq_queue *q;
16351da177e4SLinus Torvalds
163604a56dd8STakashi Sakamoto q = queueptr(info->queue);
16371da177e4SLinus Torvalds if (q == NULL)
16381da177e4SLinus Torvalds return -EINVAL;
16391da177e4SLinus Torvalds
164004a56dd8STakashi Sakamoto memset(info, 0, sizeof(*info));
164104a56dd8STakashi Sakamoto info->queue = q->queue;
164204a56dd8STakashi Sakamoto info->owner = q->owner;
164304a56dd8STakashi Sakamoto info->locked = q->locked;
164475b1a8f9SJoe Perches strscpy(info->name, q->name, sizeof(info->name));
16451da177e4SLinus Torvalds queuefree(q);
16461da177e4SLinus Torvalds
16471da177e4SLinus Torvalds return 0;
16481da177e4SLinus Torvalds }
16491da177e4SLinus Torvalds
16501da177e4SLinus Torvalds /* SET_QUEUE_INFO ioctl() */
snd_seq_ioctl_set_queue_info(struct snd_seq_client * client,void * arg)1651c7e0b5bfSTakashi Iwai static int snd_seq_ioctl_set_queue_info(struct snd_seq_client *client,
165204a56dd8STakashi Sakamoto void *arg)
16531da177e4SLinus Torvalds {
165404a56dd8STakashi Sakamoto struct snd_seq_queue_info *info = arg;
1655c7e0b5bfSTakashi Iwai struct snd_seq_queue *q;
16561da177e4SLinus Torvalds
165704a56dd8STakashi Sakamoto if (info->owner != client->number)
16581da177e4SLinus Torvalds return -EINVAL;
16591da177e4SLinus Torvalds
16601da177e4SLinus Torvalds /* change owner/locked permission */
166104a56dd8STakashi Sakamoto if (snd_seq_queue_check_access(info->queue, client->number)) {
166204a56dd8STakashi Sakamoto if (snd_seq_queue_set_owner(info->queue, client->number, info->locked) < 0)
16631da177e4SLinus Torvalds return -EPERM;
166404a56dd8STakashi Sakamoto if (info->locked)
166504a56dd8STakashi Sakamoto snd_seq_queue_use(info->queue, client->number, 1);
16661da177e4SLinus Torvalds } else {
16671da177e4SLinus Torvalds return -EPERM;
16681da177e4SLinus Torvalds }
16691da177e4SLinus Torvalds
167004a56dd8STakashi Sakamoto q = queueptr(info->queue);
16711da177e4SLinus Torvalds if (! q)
16721da177e4SLinus Torvalds return -EINVAL;
16731da177e4SLinus Torvalds if (q->owner != client->number) {
16741da177e4SLinus Torvalds queuefree(q);
16751da177e4SLinus Torvalds return -EPERM;
16761da177e4SLinus Torvalds }
1677212ac181SZubin Mithra strscpy(q->name, info->name, sizeof(q->name));
16781da177e4SLinus Torvalds queuefree(q);
16791da177e4SLinus Torvalds
16801da177e4SLinus Torvalds return 0;
16811da177e4SLinus Torvalds }
16821da177e4SLinus Torvalds
16831da177e4SLinus Torvalds /* GET_NAMED_QUEUE ioctl() */
snd_seq_ioctl_get_named_queue(struct snd_seq_client * client,void * arg)168404a56dd8STakashi Sakamoto static int snd_seq_ioctl_get_named_queue(struct snd_seq_client *client,
168504a56dd8STakashi Sakamoto void *arg)
16861da177e4SLinus Torvalds {
168704a56dd8STakashi Sakamoto struct snd_seq_queue_info *info = arg;
1688c7e0b5bfSTakashi Iwai struct snd_seq_queue *q;
16891da177e4SLinus Torvalds
169004a56dd8STakashi Sakamoto q = snd_seq_queue_find_name(info->name);
16911da177e4SLinus Torvalds if (q == NULL)
16921da177e4SLinus Torvalds return -EINVAL;
169304a56dd8STakashi Sakamoto info->queue = q->queue;
169404a56dd8STakashi Sakamoto info->owner = q->owner;
169504a56dd8STakashi Sakamoto info->locked = q->locked;
16961da177e4SLinus Torvalds queuefree(q);
16971da177e4SLinus Torvalds
16981da177e4SLinus Torvalds return 0;
16991da177e4SLinus Torvalds }
17001da177e4SLinus Torvalds
17011da177e4SLinus Torvalds /* GET_QUEUE_STATUS ioctl() */
snd_seq_ioctl_get_queue_status(struct snd_seq_client * client,void * arg)1702c7e0b5bfSTakashi Iwai static int snd_seq_ioctl_get_queue_status(struct snd_seq_client *client,
170304a56dd8STakashi Sakamoto void *arg)
17041da177e4SLinus Torvalds {
170504a56dd8STakashi Sakamoto struct snd_seq_queue_status *status = arg;
1706c7e0b5bfSTakashi Iwai struct snd_seq_queue *queue;
1707c7e0b5bfSTakashi Iwai struct snd_seq_timer *tmr;
17081da177e4SLinus Torvalds
170904a56dd8STakashi Sakamoto queue = queueptr(status->queue);
17101da177e4SLinus Torvalds if (queue == NULL)
17111da177e4SLinus Torvalds return -EINVAL;
171204a56dd8STakashi Sakamoto memset(status, 0, sizeof(*status));
171304a56dd8STakashi Sakamoto status->queue = queue->queue;
17141da177e4SLinus Torvalds
17151da177e4SLinus Torvalds tmr = queue->timer;
171604a56dd8STakashi Sakamoto status->events = queue->tickq->cells + queue->timeq->cells;
17171da177e4SLinus Torvalds
1718dc749779STakashi Iwai status->time = snd_seq_timer_get_cur_time(tmr, true);
171904a56dd8STakashi Sakamoto status->tick = snd_seq_timer_get_cur_tick(tmr);
17201da177e4SLinus Torvalds
172104a56dd8STakashi Sakamoto status->running = tmr->running;
17221da177e4SLinus Torvalds
172304a56dd8STakashi Sakamoto status->flags = queue->flags;
17241da177e4SLinus Torvalds queuefree(queue);
17251da177e4SLinus Torvalds
17261da177e4SLinus Torvalds return 0;
17271da177e4SLinus Torvalds }
17281da177e4SLinus Torvalds
17291da177e4SLinus Torvalds
17301da177e4SLinus Torvalds /* GET_QUEUE_TEMPO ioctl() */
snd_seq_ioctl_get_queue_tempo(struct snd_seq_client * client,void * arg)1731c7e0b5bfSTakashi Iwai static int snd_seq_ioctl_get_queue_tempo(struct snd_seq_client *client,
173204a56dd8STakashi Sakamoto void *arg)
17331da177e4SLinus Torvalds {
173404a56dd8STakashi Sakamoto struct snd_seq_queue_tempo *tempo = arg;
1735c7e0b5bfSTakashi Iwai struct snd_seq_queue *queue;
1736c7e0b5bfSTakashi Iwai struct snd_seq_timer *tmr;
17371da177e4SLinus Torvalds
173804a56dd8STakashi Sakamoto queue = queueptr(tempo->queue);
17391da177e4SLinus Torvalds if (queue == NULL)
17401da177e4SLinus Torvalds return -EINVAL;
174104a56dd8STakashi Sakamoto memset(tempo, 0, sizeof(*tempo));
174204a56dd8STakashi Sakamoto tempo->queue = queue->queue;
17431da177e4SLinus Torvalds
17441da177e4SLinus Torvalds tmr = queue->timer;
17451da177e4SLinus Torvalds
174604a56dd8STakashi Sakamoto tempo->tempo = tmr->tempo;
174704a56dd8STakashi Sakamoto tempo->ppq = tmr->ppq;
174804a56dd8STakashi Sakamoto tempo->skew_value = tmr->skew;
174904a56dd8STakashi Sakamoto tempo->skew_base = tmr->skew_base;
17501da177e4SLinus Torvalds queuefree(queue);
17511da177e4SLinus Torvalds
17521da177e4SLinus Torvalds return 0;
17531da177e4SLinus Torvalds }
17541da177e4SLinus Torvalds
17551da177e4SLinus Torvalds
17561da177e4SLinus Torvalds /* SET_QUEUE_TEMPO ioctl() */
snd_seq_set_queue_tempo(int client,struct snd_seq_queue_tempo * tempo)1757c7e0b5bfSTakashi Iwai int snd_seq_set_queue_tempo(int client, struct snd_seq_queue_tempo *tempo)
17581da177e4SLinus Torvalds {
17591da177e4SLinus Torvalds if (!snd_seq_queue_check_access(tempo->queue, client))
17601da177e4SLinus Torvalds return -EPERM;
17611da177e4SLinus Torvalds return snd_seq_queue_timer_set_tempo(tempo->queue, client, tempo);
17621da177e4SLinus Torvalds }
176391715ed9STakashi Iwai EXPORT_SYMBOL(snd_seq_set_queue_tempo);
176491715ed9STakashi Iwai
snd_seq_ioctl_set_queue_tempo(struct snd_seq_client * client,void * arg)1765c7e0b5bfSTakashi Iwai static int snd_seq_ioctl_set_queue_tempo(struct snd_seq_client *client,
176604a56dd8STakashi Sakamoto void *arg)
17671da177e4SLinus Torvalds {
176804a56dd8STakashi Sakamoto struct snd_seq_queue_tempo *tempo = arg;
17691da177e4SLinus Torvalds int result;
17701da177e4SLinus Torvalds
177104a56dd8STakashi Sakamoto result = snd_seq_set_queue_tempo(client->number, tempo);
17721da177e4SLinus Torvalds return result < 0 ? result : 0;
17731da177e4SLinus Torvalds }
17741da177e4SLinus Torvalds
17751da177e4SLinus Torvalds
17761da177e4SLinus Torvalds /* GET_QUEUE_TIMER ioctl() */
snd_seq_ioctl_get_queue_timer(struct snd_seq_client * client,void * arg)1777c7e0b5bfSTakashi Iwai static int snd_seq_ioctl_get_queue_timer(struct snd_seq_client *client,
177804a56dd8STakashi Sakamoto void *arg)
17791da177e4SLinus Torvalds {
178004a56dd8STakashi Sakamoto struct snd_seq_queue_timer *timer = arg;
1781c7e0b5bfSTakashi Iwai struct snd_seq_queue *queue;
1782c7e0b5bfSTakashi Iwai struct snd_seq_timer *tmr;
17831da177e4SLinus Torvalds
178404a56dd8STakashi Sakamoto queue = queueptr(timer->queue);
17851da177e4SLinus Torvalds if (queue == NULL)
17861da177e4SLinus Torvalds return -EINVAL;
17871da177e4SLinus Torvalds
178804702e8dSTakashi Iwai mutex_lock(&queue->timer_mutex);
17891da177e4SLinus Torvalds tmr = queue->timer;
179004a56dd8STakashi Sakamoto memset(timer, 0, sizeof(*timer));
179104a56dd8STakashi Sakamoto timer->queue = queue->queue;
17921da177e4SLinus Torvalds
179304a56dd8STakashi Sakamoto timer->type = tmr->type;
17941da177e4SLinus Torvalds if (tmr->type == SNDRV_SEQ_TIMER_ALSA) {
179504a56dd8STakashi Sakamoto timer->u.alsa.id = tmr->alsa_id;
179604a56dd8STakashi Sakamoto timer->u.alsa.resolution = tmr->preferred_resolution;
17971da177e4SLinus Torvalds }
17981a60d4c5SIngo Molnar mutex_unlock(&queue->timer_mutex);
17991da177e4SLinus Torvalds queuefree(queue);
18001da177e4SLinus Torvalds
18011da177e4SLinus Torvalds return 0;
18021da177e4SLinus Torvalds }
18031da177e4SLinus Torvalds
18041da177e4SLinus Torvalds
18051da177e4SLinus Torvalds /* SET_QUEUE_TIMER ioctl() */
snd_seq_ioctl_set_queue_timer(struct snd_seq_client * client,void * arg)1806c7e0b5bfSTakashi Iwai static int snd_seq_ioctl_set_queue_timer(struct snd_seq_client *client,
180704a56dd8STakashi Sakamoto void *arg)
18081da177e4SLinus Torvalds {
180904a56dd8STakashi Sakamoto struct snd_seq_queue_timer *timer = arg;
18101da177e4SLinus Torvalds int result = 0;
18111da177e4SLinus Torvalds
181204a56dd8STakashi Sakamoto if (timer->type != SNDRV_SEQ_TIMER_ALSA)
18131da177e4SLinus Torvalds return -EINVAL;
18141da177e4SLinus Torvalds
181504a56dd8STakashi Sakamoto if (snd_seq_queue_check_access(timer->queue, client->number)) {
1816c7e0b5bfSTakashi Iwai struct snd_seq_queue *q;
1817c7e0b5bfSTakashi Iwai struct snd_seq_timer *tmr;
18181da177e4SLinus Torvalds
181904a56dd8STakashi Sakamoto q = queueptr(timer->queue);
18201da177e4SLinus Torvalds if (q == NULL)
18211da177e4SLinus Torvalds return -ENXIO;
182204702e8dSTakashi Iwai mutex_lock(&q->timer_mutex);
18231da177e4SLinus Torvalds tmr = q->timer;
182404a56dd8STakashi Sakamoto snd_seq_queue_timer_close(timer->queue);
182504a56dd8STakashi Sakamoto tmr->type = timer->type;
18261da177e4SLinus Torvalds if (tmr->type == SNDRV_SEQ_TIMER_ALSA) {
182704a56dd8STakashi Sakamoto tmr->alsa_id = timer->u.alsa.id;
182804a56dd8STakashi Sakamoto tmr->preferred_resolution = timer->u.alsa.resolution;
18291da177e4SLinus Torvalds }
183004a56dd8STakashi Sakamoto result = snd_seq_queue_timer_open(timer->queue);
18311a60d4c5SIngo Molnar mutex_unlock(&q->timer_mutex);
18321da177e4SLinus Torvalds queuefree(q);
18331da177e4SLinus Torvalds } else {
18341da177e4SLinus Torvalds return -EPERM;
18351da177e4SLinus Torvalds }
18361da177e4SLinus Torvalds
18371da177e4SLinus Torvalds return result;
18381da177e4SLinus Torvalds }
18391da177e4SLinus Torvalds
18401da177e4SLinus Torvalds
18411da177e4SLinus Torvalds /* GET_QUEUE_CLIENT ioctl() */
snd_seq_ioctl_get_queue_client(struct snd_seq_client * client,void * arg)1842c7e0b5bfSTakashi Iwai static int snd_seq_ioctl_get_queue_client(struct snd_seq_client *client,
184304a56dd8STakashi Sakamoto void *arg)
18441da177e4SLinus Torvalds {
184504a56dd8STakashi Sakamoto struct snd_seq_queue_client *info = arg;
18461da177e4SLinus Torvalds int used;
18471da177e4SLinus Torvalds
184804a56dd8STakashi Sakamoto used = snd_seq_queue_is_used(info->queue, client->number);
18491da177e4SLinus Torvalds if (used < 0)
18501da177e4SLinus Torvalds return -EINVAL;
185104a56dd8STakashi Sakamoto info->used = used;
185204a56dd8STakashi Sakamoto info->client = client->number;
18531da177e4SLinus Torvalds
18541da177e4SLinus Torvalds return 0;
18551da177e4SLinus Torvalds }
18561da177e4SLinus Torvalds
18571da177e4SLinus Torvalds
18581da177e4SLinus Torvalds /* SET_QUEUE_CLIENT ioctl() */
snd_seq_ioctl_set_queue_client(struct snd_seq_client * client,void * arg)1859c7e0b5bfSTakashi Iwai static int snd_seq_ioctl_set_queue_client(struct snd_seq_client *client,
186004a56dd8STakashi Sakamoto void *arg)
18611da177e4SLinus Torvalds {
186204a56dd8STakashi Sakamoto struct snd_seq_queue_client *info = arg;
18631da177e4SLinus Torvalds int err;
18641da177e4SLinus Torvalds
186504a56dd8STakashi Sakamoto if (info->used >= 0) {
186604a56dd8STakashi Sakamoto err = snd_seq_queue_use(info->queue, client->number, info->used);
18671da177e4SLinus Torvalds if (err < 0)
18681da177e4SLinus Torvalds return err;
18691da177e4SLinus Torvalds }
18701da177e4SLinus Torvalds
18711da177e4SLinus Torvalds return snd_seq_ioctl_get_queue_client(client, arg);
18721da177e4SLinus Torvalds }
18731da177e4SLinus Torvalds
18741da177e4SLinus Torvalds
18751da177e4SLinus Torvalds /* GET_CLIENT_POOL ioctl() */
snd_seq_ioctl_get_client_pool(struct snd_seq_client * client,void * arg)1876c7e0b5bfSTakashi Iwai static int snd_seq_ioctl_get_client_pool(struct snd_seq_client *client,
187704a56dd8STakashi Sakamoto void *arg)
18781da177e4SLinus Torvalds {
187904a56dd8STakashi Sakamoto struct snd_seq_client_pool *info = arg;
1880c7e0b5bfSTakashi Iwai struct snd_seq_client *cptr;
18811da177e4SLinus Torvalds
1882*5e1b3bf7STakashi Iwai cptr = client_load_and_use_ptr(info->client);
18831da177e4SLinus Torvalds if (cptr == NULL)
18841da177e4SLinus Torvalds return -ENOENT;
188504a56dd8STakashi Sakamoto memset(info, 0, sizeof(*info));
188604a56dd8STakashi Sakamoto info->client = cptr->number;
188704a56dd8STakashi Sakamoto info->output_pool = cptr->pool->size;
188804a56dd8STakashi Sakamoto info->output_room = cptr->pool->room;
188904a56dd8STakashi Sakamoto info->output_free = info->output_pool;
189004a56dd8STakashi Sakamoto info->output_free = snd_seq_unused_cells(cptr->pool);
18911da177e4SLinus Torvalds if (cptr->type == USER_CLIENT) {
189204a56dd8STakashi Sakamoto info->input_pool = cptr->data.user.fifo_pool_size;
189304a56dd8STakashi Sakamoto info->input_free = info->input_pool;
189475545304STakashi Iwai info->input_free = snd_seq_fifo_unused_cells(cptr->data.user.fifo);
18951da177e4SLinus Torvalds } else {
189604a56dd8STakashi Sakamoto info->input_pool = 0;
189704a56dd8STakashi Sakamoto info->input_free = 0;
18981da177e4SLinus Torvalds }
18991da177e4SLinus Torvalds snd_seq_client_unlock(cptr);
19001da177e4SLinus Torvalds
19011da177e4SLinus Torvalds return 0;
19021da177e4SLinus Torvalds }
19031da177e4SLinus Torvalds
19041da177e4SLinus Torvalds /* SET_CLIENT_POOL ioctl() */
snd_seq_ioctl_set_client_pool(struct snd_seq_client * client,void * arg)1905c7e0b5bfSTakashi Iwai static int snd_seq_ioctl_set_client_pool(struct snd_seq_client *client,
190604a56dd8STakashi Sakamoto void *arg)
19071da177e4SLinus Torvalds {
190804a56dd8STakashi Sakamoto struct snd_seq_client_pool *info = arg;
19091da177e4SLinus Torvalds int rc;
19101da177e4SLinus Torvalds
191104a56dd8STakashi Sakamoto if (client->number != info->client)
19121da177e4SLinus Torvalds return -EINVAL; /* can't change other clients */
19131da177e4SLinus Torvalds
191404a56dd8STakashi Sakamoto if (info->output_pool >= 1 && info->output_pool <= SNDRV_SEQ_MAX_EVENTS &&
19151da177e4SLinus Torvalds (! snd_seq_write_pool_allocated(client) ||
191604a56dd8STakashi Sakamoto info->output_pool != client->pool->size)) {
19171da177e4SLinus Torvalds if (snd_seq_write_pool_allocated(client)) {
1918d8573936STakashi Iwai /* is the pool in use? */
1919d8573936STakashi Iwai if (atomic_read(&client->pool->counter))
1920d8573936STakashi Iwai return -EBUSY;
19211da177e4SLinus Torvalds /* remove all existing cells */
1922c520ff3dSTakashi Iwai snd_seq_pool_mark_closing(client->pool);
19231da177e4SLinus Torvalds snd_seq_pool_done(client->pool);
19241da177e4SLinus Torvalds }
192504a56dd8STakashi Sakamoto client->pool->size = info->output_pool;
19261da177e4SLinus Torvalds rc = snd_seq_pool_init(client->pool);
19271da177e4SLinus Torvalds if (rc < 0)
19281da177e4SLinus Torvalds return rc;
19291da177e4SLinus Torvalds }
19301da177e4SLinus Torvalds if (client->type == USER_CLIENT && client->data.user.fifo != NULL &&
193104a56dd8STakashi Sakamoto info->input_pool >= 1 &&
193204a56dd8STakashi Sakamoto info->input_pool <= SNDRV_SEQ_MAX_CLIENT_EVENTS &&
193304a56dd8STakashi Sakamoto info->input_pool != client->data.user.fifo_pool_size) {
19341da177e4SLinus Torvalds /* change pool size */
193504a56dd8STakashi Sakamoto rc = snd_seq_fifo_resize(client->data.user.fifo, info->input_pool);
19361da177e4SLinus Torvalds if (rc < 0)
19371da177e4SLinus Torvalds return rc;
193804a56dd8STakashi Sakamoto client->data.user.fifo_pool_size = info->input_pool;
19391da177e4SLinus Torvalds }
194004a56dd8STakashi Sakamoto if (info->output_room >= 1 &&
194104a56dd8STakashi Sakamoto info->output_room <= client->pool->size) {
194204a56dd8STakashi Sakamoto client->pool->room = info->output_room;
19431da177e4SLinus Torvalds }
19441da177e4SLinus Torvalds
19451da177e4SLinus Torvalds return snd_seq_ioctl_get_client_pool(client, arg);
19461da177e4SLinus Torvalds }
19471da177e4SLinus Torvalds
19481da177e4SLinus Torvalds
19491da177e4SLinus Torvalds /* REMOVE_EVENTS ioctl() */
snd_seq_ioctl_remove_events(struct snd_seq_client * client,void * arg)1950c7e0b5bfSTakashi Iwai static int snd_seq_ioctl_remove_events(struct snd_seq_client *client,
195104a56dd8STakashi Sakamoto void *arg)
19521da177e4SLinus Torvalds {
195304a56dd8STakashi Sakamoto struct snd_seq_remove_events *info = arg;
19541da177e4SLinus Torvalds
19551da177e4SLinus Torvalds /*
19561da177e4SLinus Torvalds * Input mostly not implemented XXX.
19571da177e4SLinus Torvalds */
195804a56dd8STakashi Sakamoto if (info->remove_mode & SNDRV_SEQ_REMOVE_INPUT) {
19591da177e4SLinus Torvalds /*
19601da177e4SLinus Torvalds * No restrictions so for a user client we can clear
19611da177e4SLinus Torvalds * the whole fifo
19621da177e4SLinus Torvalds */
1963030e2c78STakashi Iwai if (client->type == USER_CLIENT && client->data.user.fifo)
19641da177e4SLinus Torvalds snd_seq_fifo_clear(client->data.user.fifo);
19651da177e4SLinus Torvalds }
19661da177e4SLinus Torvalds
196704a56dd8STakashi Sakamoto if (info->remove_mode & SNDRV_SEQ_REMOVE_OUTPUT)
196804a56dd8STakashi Sakamoto snd_seq_queue_remove_cells(client->number, info);
19691da177e4SLinus Torvalds
19701da177e4SLinus Torvalds return 0;
19711da177e4SLinus Torvalds }
19721da177e4SLinus Torvalds
19731da177e4SLinus Torvalds
19741da177e4SLinus Torvalds /*
19751da177e4SLinus Torvalds * get subscription info
19761da177e4SLinus Torvalds */
snd_seq_ioctl_get_subscription(struct snd_seq_client * client,void * arg)1977c7e0b5bfSTakashi Iwai static int snd_seq_ioctl_get_subscription(struct snd_seq_client *client,
197804a56dd8STakashi Sakamoto void *arg)
19791da177e4SLinus Torvalds {
198004a56dd8STakashi Sakamoto struct snd_seq_port_subscribe *subs = arg;
19811da177e4SLinus Torvalds int result;
1982c7e0b5bfSTakashi Iwai struct snd_seq_client *sender = NULL;
1983c7e0b5bfSTakashi Iwai struct snd_seq_client_port *sport = NULL;
19841da177e4SLinus Torvalds
19851da177e4SLinus Torvalds result = -EINVAL;
1986*5e1b3bf7STakashi Iwai sender = client_load_and_use_ptr(subs->sender.client);
1987f9a6bb84STakashi Iwai if (!sender)
19881da177e4SLinus Torvalds goto __end;
1989f9a6bb84STakashi Iwai sport = snd_seq_port_use_ptr(sender, subs->sender.port);
1990f9a6bb84STakashi Iwai if (!sport)
19911da177e4SLinus Torvalds goto __end;
19922eabc5ecSTakashi Iwai result = snd_seq_port_get_subscription(&sport->c_src, &subs->dest,
19932eabc5ecSTakashi Iwai subs);
19941da177e4SLinus Torvalds __end:
19951da177e4SLinus Torvalds if (sport)
19961da177e4SLinus Torvalds snd_seq_port_unlock(sport);
19971da177e4SLinus Torvalds if (sender)
19981da177e4SLinus Torvalds snd_seq_client_unlock(sender);
199904a56dd8STakashi Sakamoto
20001da177e4SLinus Torvalds return result;
20011da177e4SLinus Torvalds }
20021da177e4SLinus Torvalds
20031da177e4SLinus Torvalds
20041da177e4SLinus Torvalds /*
20051da177e4SLinus Torvalds * get subscription info - check only its presence
20061da177e4SLinus Torvalds */
snd_seq_ioctl_query_subs(struct snd_seq_client * client,void * arg)200704a56dd8STakashi Sakamoto static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, void *arg)
20081da177e4SLinus Torvalds {
200904a56dd8STakashi Sakamoto struct snd_seq_query_subs *subs = arg;
20101da177e4SLinus Torvalds int result = -ENXIO;
2011c7e0b5bfSTakashi Iwai struct snd_seq_client *cptr = NULL;
2012c7e0b5bfSTakashi Iwai struct snd_seq_client_port *port = NULL;
2013c7e0b5bfSTakashi Iwai struct snd_seq_port_subs_info *group;
20141da177e4SLinus Torvalds struct list_head *p;
20151da177e4SLinus Torvalds int i;
20161da177e4SLinus Torvalds
2017*5e1b3bf7STakashi Iwai cptr = client_load_and_use_ptr(subs->root.client);
2018f9a6bb84STakashi Iwai if (!cptr)
20191da177e4SLinus Torvalds goto __end;
2020f9a6bb84STakashi Iwai port = snd_seq_port_use_ptr(cptr, subs->root.port);
2021f9a6bb84STakashi Iwai if (!port)
20221da177e4SLinus Torvalds goto __end;
20231da177e4SLinus Torvalds
202404a56dd8STakashi Sakamoto switch (subs->type) {
20251da177e4SLinus Torvalds case SNDRV_SEQ_QUERY_SUBS_READ:
20261da177e4SLinus Torvalds group = &port->c_src;
20271da177e4SLinus Torvalds break;
20281da177e4SLinus Torvalds case SNDRV_SEQ_QUERY_SUBS_WRITE:
20291da177e4SLinus Torvalds group = &port->c_dest;
20301da177e4SLinus Torvalds break;
20311da177e4SLinus Torvalds default:
20321da177e4SLinus Torvalds goto __end;
20331da177e4SLinus Torvalds }
20341da177e4SLinus Torvalds
20351da177e4SLinus Torvalds down_read(&group->list_mutex);
20361da177e4SLinus Torvalds /* search for the subscriber */
203704a56dd8STakashi Sakamoto subs->num_subs = group->count;
20381da177e4SLinus Torvalds i = 0;
20391da177e4SLinus Torvalds result = -ENOENT;
20401da177e4SLinus Torvalds list_for_each(p, &group->list_head) {
204104a56dd8STakashi Sakamoto if (i++ == subs->index) {
20421da177e4SLinus Torvalds /* found! */
2043c7e0b5bfSTakashi Iwai struct snd_seq_subscribers *s;
204404a56dd8STakashi Sakamoto if (subs->type == SNDRV_SEQ_QUERY_SUBS_READ) {
2045c7e0b5bfSTakashi Iwai s = list_entry(p, struct snd_seq_subscribers, src_list);
204604a56dd8STakashi Sakamoto subs->addr = s->info.dest;
20471da177e4SLinus Torvalds } else {
2048c7e0b5bfSTakashi Iwai s = list_entry(p, struct snd_seq_subscribers, dest_list);
204904a56dd8STakashi Sakamoto subs->addr = s->info.sender;
20501da177e4SLinus Torvalds }
205104a56dd8STakashi Sakamoto subs->flags = s->info.flags;
205204a56dd8STakashi Sakamoto subs->queue = s->info.queue;
20531da177e4SLinus Torvalds result = 0;
20541da177e4SLinus Torvalds break;
20551da177e4SLinus Torvalds }
20561da177e4SLinus Torvalds }
20571da177e4SLinus Torvalds up_read(&group->list_mutex);
20581da177e4SLinus Torvalds
20591da177e4SLinus Torvalds __end:
20601da177e4SLinus Torvalds if (port)
20611da177e4SLinus Torvalds snd_seq_port_unlock(port);
20621da177e4SLinus Torvalds if (cptr)
20631da177e4SLinus Torvalds snd_seq_client_unlock(cptr);
206404a56dd8STakashi Sakamoto
20651da177e4SLinus Torvalds return result;
20661da177e4SLinus Torvalds }
20671da177e4SLinus Torvalds
20681da177e4SLinus Torvalds
20691da177e4SLinus Torvalds /*
20701da177e4SLinus Torvalds * query next client
20711da177e4SLinus Torvalds */
snd_seq_ioctl_query_next_client(struct snd_seq_client * client,void * arg)2072c7e0b5bfSTakashi Iwai static int snd_seq_ioctl_query_next_client(struct snd_seq_client *client,
207304a56dd8STakashi Sakamoto void *arg)
20741da177e4SLinus Torvalds {
207504a56dd8STakashi Sakamoto struct snd_seq_client_info *info = arg;
2076c7e0b5bfSTakashi Iwai struct snd_seq_client *cptr = NULL;
20771da177e4SLinus Torvalds
20781da177e4SLinus Torvalds /* search for next client */
2079c9a4c638STakashi Iwai if (info->client < INT_MAX)
208004a56dd8STakashi Sakamoto info->client++;
208104a56dd8STakashi Sakamoto if (info->client < 0)
208204a56dd8STakashi Sakamoto info->client = 0;
208304a56dd8STakashi Sakamoto for (; info->client < SNDRV_SEQ_MAX_CLIENTS; info->client++) {
2084*5e1b3bf7STakashi Iwai cptr = client_load_and_use_ptr(info->client);
20851da177e4SLinus Torvalds if (cptr)
20861da177e4SLinus Torvalds break; /* found */
20871da177e4SLinus Torvalds }
20881da177e4SLinus Torvalds if (cptr == NULL)
20891da177e4SLinus Torvalds return -ENOENT;
20901da177e4SLinus Torvalds
209104a56dd8STakashi Sakamoto get_client_info(cptr, info);
20921da177e4SLinus Torvalds snd_seq_client_unlock(cptr);
20931da177e4SLinus Torvalds
20941da177e4SLinus Torvalds return 0;
20951da177e4SLinus Torvalds }
20961da177e4SLinus Torvalds
20971da177e4SLinus Torvalds /*
20981da177e4SLinus Torvalds * query next port
20991da177e4SLinus Torvalds */
snd_seq_ioctl_query_next_port(struct snd_seq_client * client,void * arg)2100c7e0b5bfSTakashi Iwai static int snd_seq_ioctl_query_next_port(struct snd_seq_client *client,
210104a56dd8STakashi Sakamoto void *arg)
21021da177e4SLinus Torvalds {
210304a56dd8STakashi Sakamoto struct snd_seq_port_info *info = arg;
2104c7e0b5bfSTakashi Iwai struct snd_seq_client *cptr;
2105c7e0b5bfSTakashi Iwai struct snd_seq_client_port *port = NULL;
21061da177e4SLinus Torvalds
2107*5e1b3bf7STakashi Iwai cptr = client_load_and_use_ptr(info->addr.client);
21081da177e4SLinus Torvalds if (cptr == NULL)
21091da177e4SLinus Torvalds return -ENXIO;
21101da177e4SLinus Torvalds
21111da177e4SLinus Torvalds /* search for next port */
211204a56dd8STakashi Sakamoto info->addr.port++;
211304a56dd8STakashi Sakamoto port = snd_seq_port_query_nearest(cptr, info);
21141da177e4SLinus Torvalds if (port == NULL) {
21151da177e4SLinus Torvalds snd_seq_client_unlock(cptr);
21161da177e4SLinus Torvalds return -ENOENT;
21171da177e4SLinus Torvalds }
21181da177e4SLinus Torvalds
21191da177e4SLinus Torvalds /* get port info */
212004a56dd8STakashi Sakamoto info->addr = port->addr;
212104a56dd8STakashi Sakamoto snd_seq_get_port_info(port, info);
21221da177e4SLinus Torvalds snd_seq_port_unlock(port);
21231da177e4SLinus Torvalds snd_seq_client_unlock(cptr);
21241da177e4SLinus Torvalds
21251da177e4SLinus Torvalds return 0;
21261da177e4SLinus Torvalds }
21271da177e4SLinus Torvalds
2128d2d247e3STakashi Iwai #if IS_ENABLED(CONFIG_SND_SEQ_UMP)
2129d2d247e3STakashi Iwai #define NUM_UMP_INFOS (SNDRV_UMP_MAX_BLOCKS + 1)
2130d2d247e3STakashi Iwai
free_ump_info(struct snd_seq_client * client)2131d2d247e3STakashi Iwai static void free_ump_info(struct snd_seq_client *client)
2132d2d247e3STakashi Iwai {
2133d2d247e3STakashi Iwai int i;
2134d2d247e3STakashi Iwai
2135d2d247e3STakashi Iwai if (!client->ump_info)
2136d2d247e3STakashi Iwai return;
2137d2d247e3STakashi Iwai for (i = 0; i < NUM_UMP_INFOS; i++)
2138d2d247e3STakashi Iwai kfree(client->ump_info[i]);
2139d2d247e3STakashi Iwai kfree(client->ump_info);
2140d2d247e3STakashi Iwai client->ump_info = NULL;
2141d2d247e3STakashi Iwai }
2142d2d247e3STakashi Iwai
terminate_ump_info_strings(void * p,int type)2143d2d247e3STakashi Iwai static void terminate_ump_info_strings(void *p, int type)
2144d2d247e3STakashi Iwai {
2145d2d247e3STakashi Iwai if (type == SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT) {
2146d2d247e3STakashi Iwai struct snd_ump_endpoint_info *ep = p;
2147d2d247e3STakashi Iwai ep->name[sizeof(ep->name) - 1] = 0;
2148d2d247e3STakashi Iwai } else {
2149d2d247e3STakashi Iwai struct snd_ump_block_info *bp = p;
2150d2d247e3STakashi Iwai bp->name[sizeof(bp->name) - 1] = 0;
2151d2d247e3STakashi Iwai }
2152d2d247e3STakashi Iwai }
2153d2d247e3STakashi Iwai
2154e85b9260STakashi Iwai #ifdef CONFIG_SND_PROC_FS
dump_ump_info(struct snd_info_buffer * buffer,struct snd_seq_client * client)2155e85b9260STakashi Iwai static void dump_ump_info(struct snd_info_buffer *buffer,
2156e85b9260STakashi Iwai struct snd_seq_client *client)
2157e85b9260STakashi Iwai {
2158e85b9260STakashi Iwai struct snd_ump_endpoint_info *ep;
2159e85b9260STakashi Iwai struct snd_ump_block_info *bp;
2160e85b9260STakashi Iwai int i;
2161e85b9260STakashi Iwai
2162e85b9260STakashi Iwai if (!client->ump_info)
2163e85b9260STakashi Iwai return;
2164e85b9260STakashi Iwai ep = client->ump_info[SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT];
2165e85b9260STakashi Iwai if (ep && *ep->name)
2166e85b9260STakashi Iwai snd_iprintf(buffer, " UMP Endpoint: \"%s\"\n", ep->name);
2167e85b9260STakashi Iwai for (i = 0; i < SNDRV_UMP_MAX_BLOCKS; i++) {
2168e85b9260STakashi Iwai bp = client->ump_info[i + 1];
2169e85b9260STakashi Iwai if (bp && *bp->name) {
2170e85b9260STakashi Iwai snd_iprintf(buffer, " UMP Block %d: \"%s\" [%s]\n",
2171e85b9260STakashi Iwai i, bp->name,
2172e85b9260STakashi Iwai bp->active ? "Active" : "Inactive");
2173e85b9260STakashi Iwai snd_iprintf(buffer, " Groups: %d-%d\n",
2174e85b9260STakashi Iwai bp->first_group + 1,
2175e85b9260STakashi Iwai bp->first_group + bp->num_groups);
2176e85b9260STakashi Iwai }
2177e85b9260STakashi Iwai }
2178e85b9260STakashi Iwai }
2179e85b9260STakashi Iwai #endif
2180e85b9260STakashi Iwai
2181d2d247e3STakashi Iwai /* UMP-specific ioctls -- called directly without data copy */
snd_seq_ioctl_client_ump_info(struct snd_seq_client * caller,unsigned int cmd,unsigned long arg)2182d2d247e3STakashi Iwai static int snd_seq_ioctl_client_ump_info(struct snd_seq_client *caller,
2183d2d247e3STakashi Iwai unsigned int cmd,
2184d2d247e3STakashi Iwai unsigned long arg)
2185d2d247e3STakashi Iwai {
2186d2d247e3STakashi Iwai struct snd_seq_client_ump_info __user *argp =
2187d2d247e3STakashi Iwai (struct snd_seq_client_ump_info __user *)arg;
2188d2d247e3STakashi Iwai struct snd_seq_client *cptr;
2189d2d247e3STakashi Iwai int client, type, err = 0;
2190d2d247e3STakashi Iwai size_t size;
2191d2d247e3STakashi Iwai void *p;
2192d2d247e3STakashi Iwai
2193d2d247e3STakashi Iwai if (get_user(client, &argp->client) || get_user(type, &argp->type))
2194d2d247e3STakashi Iwai return -EFAULT;
2195d2d247e3STakashi Iwai if (cmd == SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO &&
2196d2d247e3STakashi Iwai caller->number != client)
2197d2d247e3STakashi Iwai return -EPERM;
2198d2d247e3STakashi Iwai if (type < 0 || type >= NUM_UMP_INFOS)
2199d2d247e3STakashi Iwai return -EINVAL;
2200d2d247e3STakashi Iwai if (type == SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT)
2201d2d247e3STakashi Iwai size = sizeof(struct snd_ump_endpoint_info);
2202d2d247e3STakashi Iwai else
2203d2d247e3STakashi Iwai size = sizeof(struct snd_ump_block_info);
2204*5e1b3bf7STakashi Iwai cptr = client_load_and_use_ptr(client);
2205d2d247e3STakashi Iwai if (!cptr)
2206d2d247e3STakashi Iwai return -ENOENT;
2207d2d247e3STakashi Iwai
2208d2d247e3STakashi Iwai mutex_lock(&cptr->ioctl_mutex);
2209d2d247e3STakashi Iwai if (!cptr->midi_version) {
2210d2d247e3STakashi Iwai err = -EBADFD;
2211d2d247e3STakashi Iwai goto error;
2212d2d247e3STakashi Iwai }
2213d2d247e3STakashi Iwai
2214d2d247e3STakashi Iwai if (cmd == SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO) {
2215d2d247e3STakashi Iwai if (!cptr->ump_info)
2216d2d247e3STakashi Iwai p = NULL;
2217d2d247e3STakashi Iwai else
2218d2d247e3STakashi Iwai p = cptr->ump_info[type];
2219d2d247e3STakashi Iwai if (!p) {
2220d2d247e3STakashi Iwai err = -ENODEV;
2221d2d247e3STakashi Iwai goto error;
2222d2d247e3STakashi Iwai }
2223d2d247e3STakashi Iwai if (copy_to_user(argp->info, p, size)) {
2224d2d247e3STakashi Iwai err = -EFAULT;
2225d2d247e3STakashi Iwai goto error;
2226d2d247e3STakashi Iwai }
2227d2d247e3STakashi Iwai } else {
2228d2d247e3STakashi Iwai if (cptr->type != USER_CLIENT) {
2229d2d247e3STakashi Iwai err = -EBADFD;
2230d2d247e3STakashi Iwai goto error;
2231d2d247e3STakashi Iwai }
2232d2d247e3STakashi Iwai if (!cptr->ump_info) {
2233d2d247e3STakashi Iwai cptr->ump_info = kcalloc(NUM_UMP_INFOS,
2234d2d247e3STakashi Iwai sizeof(void *), GFP_KERNEL);
2235d2d247e3STakashi Iwai if (!cptr->ump_info) {
2236d2d247e3STakashi Iwai err = -ENOMEM;
2237d2d247e3STakashi Iwai goto error;
2238d2d247e3STakashi Iwai }
2239d2d247e3STakashi Iwai }
2240d2d247e3STakashi Iwai p = memdup_user(argp->info, size);
2241d2d247e3STakashi Iwai if (IS_ERR(p)) {
2242d2d247e3STakashi Iwai err = PTR_ERR(p);
2243d2d247e3STakashi Iwai goto error;
2244d2d247e3STakashi Iwai }
2245d2d247e3STakashi Iwai kfree(cptr->ump_info[type]);
2246d2d247e3STakashi Iwai terminate_ump_info_strings(p, type);
2247d2d247e3STakashi Iwai cptr->ump_info[type] = p;
2248d2d247e3STakashi Iwai }
2249d2d247e3STakashi Iwai
2250d2d247e3STakashi Iwai error:
2251d2d247e3STakashi Iwai mutex_unlock(&cptr->ioctl_mutex);
2252d2d247e3STakashi Iwai snd_seq_client_unlock(cptr);
2253d2d247e3STakashi Iwai return err;
2254d2d247e3STakashi Iwai }
2255d2d247e3STakashi Iwai #endif
2256d2d247e3STakashi Iwai
22571da177e4SLinus Torvalds /* -------------------------------------------------------- */
22581da177e4SLinus Torvalds
22598ce8eb60STakashi Sakamoto static const struct ioctl_handler {
22608ce8eb60STakashi Sakamoto unsigned int cmd;
22618ce8eb60STakashi Sakamoto int (*func)(struct snd_seq_client *client, void *arg);
22628ce8eb60STakashi Sakamoto } ioctl_handlers[] = {
226304a56dd8STakashi Sakamoto { SNDRV_SEQ_IOCTL_PVERSION, snd_seq_ioctl_pversion },
2264afb72505STakashi Iwai { SNDRV_SEQ_IOCTL_USER_PVERSION, snd_seq_ioctl_user_pversion },
226504a56dd8STakashi Sakamoto { SNDRV_SEQ_IOCTL_CLIENT_ID, snd_seq_ioctl_client_id },
22661da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_SYSTEM_INFO, snd_seq_ioctl_system_info },
22671da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_RUNNING_MODE, snd_seq_ioctl_running_mode },
22681da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_GET_CLIENT_INFO, snd_seq_ioctl_get_client_info },
22691da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, snd_seq_ioctl_set_client_info },
22701da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_CREATE_PORT, snd_seq_ioctl_create_port },
22711da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_DELETE_PORT, snd_seq_ioctl_delete_port },
22721da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_GET_PORT_INFO, snd_seq_ioctl_get_port_info },
22731da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_SET_PORT_INFO, snd_seq_ioctl_set_port_info },
22741da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, snd_seq_ioctl_subscribe_port },
22751da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, snd_seq_ioctl_unsubscribe_port },
22761da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_CREATE_QUEUE, snd_seq_ioctl_create_queue },
22771da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_DELETE_QUEUE, snd_seq_ioctl_delete_queue },
22781da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_GET_QUEUE_INFO, snd_seq_ioctl_get_queue_info },
22791da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_SET_QUEUE_INFO, snd_seq_ioctl_set_queue_info },
22801da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE, snd_seq_ioctl_get_named_queue },
22811da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS, snd_seq_ioctl_get_queue_status },
22821da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO, snd_seq_ioctl_get_queue_tempo },
22831da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO, snd_seq_ioctl_set_queue_tempo },
22841da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER, snd_seq_ioctl_get_queue_timer },
22851da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER, snd_seq_ioctl_set_queue_timer },
22861da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT, snd_seq_ioctl_get_queue_client },
22871da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT, snd_seq_ioctl_set_queue_client },
22881da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, snd_seq_ioctl_get_client_pool },
22891da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, snd_seq_ioctl_set_client_pool },
22901da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION, snd_seq_ioctl_get_subscription },
22911da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, snd_seq_ioctl_query_next_client },
22921da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, snd_seq_ioctl_query_next_port },
22931da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_REMOVE_EVENTS, snd_seq_ioctl_remove_events },
22941da177e4SLinus Torvalds { SNDRV_SEQ_IOCTL_QUERY_SUBS, snd_seq_ioctl_query_subs },
22951da177e4SLinus Torvalds { 0, NULL },
22961da177e4SLinus Torvalds };
22971da177e4SLinus Torvalds
snd_seq_ioctl(struct file * file,unsigned int cmd,unsigned long arg)2298e12ec251STakashi Sakamoto static long snd_seq_ioctl(struct file *file, unsigned int cmd,
2299e12ec251STakashi Sakamoto unsigned long arg)
23008ce8eb60STakashi Sakamoto {
23018ce8eb60STakashi Sakamoto struct snd_seq_client *client = file->private_data;
23028ce8eb60STakashi Sakamoto /* To use kernel stack for ioctl data. */
23034127e80aSTakashi Sakamoto union {
23048ce8eb60STakashi Sakamoto int pversion;
23058ce8eb60STakashi Sakamoto int client_id;
23068ce8eb60STakashi Sakamoto struct snd_seq_system_info system_info;
23078ce8eb60STakashi Sakamoto struct snd_seq_running_info running_info;
23088ce8eb60STakashi Sakamoto struct snd_seq_client_info client_info;
23098ce8eb60STakashi Sakamoto struct snd_seq_port_info port_info;
23108ce8eb60STakashi Sakamoto struct snd_seq_port_subscribe port_subscribe;
23118ce8eb60STakashi Sakamoto struct snd_seq_queue_info queue_info;
23128ce8eb60STakashi Sakamoto struct snd_seq_queue_status queue_status;
23138ce8eb60STakashi Sakamoto struct snd_seq_queue_tempo tempo;
23148ce8eb60STakashi Sakamoto struct snd_seq_queue_timer queue_timer;
23158ce8eb60STakashi Sakamoto struct snd_seq_queue_client queue_client;
23168ce8eb60STakashi Sakamoto struct snd_seq_client_pool client_pool;
23178ce8eb60STakashi Sakamoto struct snd_seq_remove_events remove_events;
23188ce8eb60STakashi Sakamoto struct snd_seq_query_subs query_subs;
23194127e80aSTakashi Sakamoto } buf;
23208ce8eb60STakashi Sakamoto const struct ioctl_handler *handler;
23218ce8eb60STakashi Sakamoto unsigned long size;
23228ce8eb60STakashi Sakamoto int err;
23238ce8eb60STakashi Sakamoto
23248ce8eb60STakashi Sakamoto if (snd_BUG_ON(!client))
23258ce8eb60STakashi Sakamoto return -ENXIO;
23268ce8eb60STakashi Sakamoto
2327d2d247e3STakashi Iwai #if IS_ENABLED(CONFIG_SND_SEQ_UMP)
2328d2d247e3STakashi Iwai /* exception - handling large data */
2329d2d247e3STakashi Iwai switch (cmd) {
2330d2d247e3STakashi Iwai case SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO:
2331d2d247e3STakashi Iwai case SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO:
2332d2d247e3STakashi Iwai return snd_seq_ioctl_client_ump_info(client, cmd, arg);
2333d2d247e3STakashi Iwai }
2334d2d247e3STakashi Iwai #endif
2335d2d247e3STakashi Iwai
23368ce8eb60STakashi Sakamoto for (handler = ioctl_handlers; handler->cmd > 0; ++handler) {
23378ce8eb60STakashi Sakamoto if (handler->cmd == cmd)
23388ce8eb60STakashi Sakamoto break;
23398ce8eb60STakashi Sakamoto }
23408ce8eb60STakashi Sakamoto if (handler->cmd == 0)
23418ce8eb60STakashi Sakamoto return -ENOTTY;
23424127e80aSTakashi Sakamoto
23434127e80aSTakashi Sakamoto memset(&buf, 0, sizeof(buf));
23444127e80aSTakashi Sakamoto
23458ce8eb60STakashi Sakamoto /*
23468ce8eb60STakashi Sakamoto * All of ioctl commands for ALSA sequencer get an argument of size
23478ce8eb60STakashi Sakamoto * within 13 bits. We can safely pick up the size from the command.
23488ce8eb60STakashi Sakamoto */
23498ce8eb60STakashi Sakamoto size = _IOC_SIZE(handler->cmd);
235069b05825STakashi Sakamoto if (handler->cmd & IOC_IN) {
23518ce8eb60STakashi Sakamoto if (copy_from_user(&buf, (const void __user *)arg, size))
23528ce8eb60STakashi Sakamoto return -EFAULT;
23538ce8eb60STakashi Sakamoto }
23548ce8eb60STakashi Sakamoto
2355b3defb79STakashi Iwai mutex_lock(&client->ioctl_mutex);
23568ce8eb60STakashi Sakamoto err = handler->func(client, &buf);
2357b3defb79STakashi Iwai mutex_unlock(&client->ioctl_mutex);
23588ce8eb60STakashi Sakamoto if (err >= 0) {
23598ce8eb60STakashi Sakamoto /* Some commands includes a bug in 'dir' field. */
23608ce8eb60STakashi Sakamoto if (handler->cmd == SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT ||
23618ce8eb60STakashi Sakamoto handler->cmd == SNDRV_SEQ_IOCTL_SET_CLIENT_POOL ||
236269b05825STakashi Sakamoto (handler->cmd & IOC_OUT))
23638ce8eb60STakashi Sakamoto if (copy_to_user((void __user *)arg, &buf, size))
23648ce8eb60STakashi Sakamoto return -EFAULT;
23658ce8eb60STakashi Sakamoto }
23668ce8eb60STakashi Sakamoto
23678ce8eb60STakashi Sakamoto return err;
23688ce8eb60STakashi Sakamoto }
23698ce8eb60STakashi Sakamoto
23701da177e4SLinus Torvalds #ifdef CONFIG_COMPAT
23711da177e4SLinus Torvalds #include "seq_compat.c"
23721da177e4SLinus Torvalds #else
23731da177e4SLinus Torvalds #define snd_seq_ioctl_compat NULL
23741da177e4SLinus Torvalds #endif
23751da177e4SLinus Torvalds
23761da177e4SLinus Torvalds /* -------------------------------------------------------- */
23771da177e4SLinus Torvalds
23781da177e4SLinus Torvalds
23791da177e4SLinus Torvalds /* exported to kernel modules */
snd_seq_create_kernel_client(struct snd_card * card,int client_index,const char * name_fmt,...)23807b6d9245SClemens Ladisch int snd_seq_create_kernel_client(struct snd_card *card, int client_index,
23817b6d9245SClemens Ladisch const char *name_fmt, ...)
23821da177e4SLinus Torvalds {
2383c7e0b5bfSTakashi Iwai struct snd_seq_client *client;
23847b6d9245SClemens Ladisch va_list args;
23851da177e4SLinus Torvalds
23867eaa943cSTakashi Iwai if (snd_BUG_ON(in_interrupt()))
23877eaa943cSTakashi Iwai return -EBUSY;
23881da177e4SLinus Torvalds
2389aa1e77e6SClemens Ladisch if (card && client_index >= SNDRV_SEQ_CLIENTS_PER_CARD)
23901da177e4SLinus Torvalds return -EINVAL;
2391aa1e77e6SClemens Ladisch if (card == NULL && client_index >= SNDRV_SEQ_GLOBAL_CLIENTS)
23921da177e4SLinus Torvalds return -EINVAL;
23931da177e4SLinus Torvalds
239404702e8dSTakashi Iwai mutex_lock(®ister_mutex);
2395d001544dSClemens Ladisch
2396d001544dSClemens Ladisch if (card) {
2397aa1e77e6SClemens Ladisch client_index += SNDRV_SEQ_GLOBAL_CLIENTS
2398aa1e77e6SClemens Ladisch + card->number * SNDRV_SEQ_CLIENTS_PER_CARD;
2399aa1e77e6SClemens Ladisch if (client_index >= SNDRV_SEQ_DYNAMIC_CLIENTS_BEGIN)
2400d001544dSClemens Ladisch client_index = -1;
2401d001544dSClemens Ladisch }
2402d001544dSClemens Ladisch
24031da177e4SLinus Torvalds /* empty write queue as default */
2404aa1e77e6SClemens Ladisch client = seq_create_client1(client_index, 0);
24051da177e4SLinus Torvalds if (client == NULL) {
24061a60d4c5SIngo Molnar mutex_unlock(®ister_mutex);
24071da177e4SLinus Torvalds return -EBUSY; /* failure code */
24081da177e4SLinus Torvalds }
24091da177e4SLinus Torvalds usage_alloc(&client_usage, 1);
24101da177e4SLinus Torvalds
241183e8ad69SClemens Ladisch client->accept_input = 1;
241283e8ad69SClemens Ladisch client->accept_output = 1;
2413a1ce94d0SMartin Koegler client->data.kernel.card = card;
2414afb72505STakashi Iwai client->user_pversion = SNDRV_SEQ_VERSION;
24151da177e4SLinus Torvalds
24167b6d9245SClemens Ladisch va_start(args, name_fmt);
24177b6d9245SClemens Ladisch vsnprintf(client->name, sizeof(client->name), name_fmt, args);
24187b6d9245SClemens Ladisch va_end(args);
24191da177e4SLinus Torvalds
24201da177e4SLinus Torvalds client->type = KERNEL_CLIENT;
24211a60d4c5SIngo Molnar mutex_unlock(®ister_mutex);
24221da177e4SLinus Torvalds
24231da177e4SLinus Torvalds /* make others aware this new client */
24241da177e4SLinus Torvalds snd_seq_system_client_ev_client_start(client->number);
24251da177e4SLinus Torvalds
24261da177e4SLinus Torvalds /* return client number to caller */
24271da177e4SLinus Torvalds return client->number;
24281da177e4SLinus Torvalds }
242991715ed9STakashi Iwai EXPORT_SYMBOL(snd_seq_create_kernel_client);
243091715ed9STakashi Iwai
24311da177e4SLinus Torvalds /* exported to kernel modules */
snd_seq_delete_kernel_client(int client)24321da177e4SLinus Torvalds int snd_seq_delete_kernel_client(int client)
24331da177e4SLinus Torvalds {
2434c7e0b5bfSTakashi Iwai struct snd_seq_client *ptr;
24351da177e4SLinus Torvalds
24367eaa943cSTakashi Iwai if (snd_BUG_ON(in_interrupt()))
24377eaa943cSTakashi Iwai return -EBUSY;
24381da177e4SLinus Torvalds
24391da177e4SLinus Torvalds ptr = clientptr(client);
24401da177e4SLinus Torvalds if (ptr == NULL)
24411da177e4SLinus Torvalds return -EINVAL;
24421da177e4SLinus Torvalds
24431da177e4SLinus Torvalds seq_free_client(ptr);
24441da177e4SLinus Torvalds kfree(ptr);
24451da177e4SLinus Torvalds return 0;
24461da177e4SLinus Torvalds }
244791715ed9STakashi Iwai EXPORT_SYMBOL(snd_seq_delete_kernel_client);
24481da177e4SLinus Torvalds
24496740ea67STakashi Iwai /*
24506740ea67STakashi Iwai * exported, called by kernel clients to enqueue events (w/o blocking)
24516740ea67STakashi Iwai *
24526740ea67STakashi Iwai * RETURN VALUE: zero if succeed, negative if error
24531da177e4SLinus Torvalds */
snd_seq_kernel_client_enqueue(int client,struct snd_seq_event * ev,struct file * file,bool blocking)24546740ea67STakashi Iwai int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event *ev,
24556740ea67STakashi Iwai struct file *file, bool blocking)
24561da177e4SLinus Torvalds {
2457c7e0b5bfSTakashi Iwai struct snd_seq_client *cptr;
24581da177e4SLinus Torvalds int result;
24591da177e4SLinus Torvalds
24607eaa943cSTakashi Iwai if (snd_BUG_ON(!ev))
24617eaa943cSTakashi Iwai return -EINVAL;
24621da177e4SLinus Torvalds
246346397622STakashi Iwai if (!snd_seq_ev_is_ump(ev)) {
24641da177e4SLinus Torvalds if (ev->type == SNDRV_SEQ_EVENT_NONE)
24651da177e4SLinus Torvalds return 0; /* ignore this */
24661da177e4SLinus Torvalds if (ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR)
24671da177e4SLinus Torvalds return -EINVAL; /* quoted events can't be enqueued */
246846397622STakashi Iwai }
24691da177e4SLinus Torvalds
24701da177e4SLinus Torvalds /* fill in client number */
24711da177e4SLinus Torvalds ev->source.client = client;
24721da177e4SLinus Torvalds
24731da177e4SLinus Torvalds if (check_event_type_and_length(ev))
24741da177e4SLinus Torvalds return -EINVAL;
24751da177e4SLinus Torvalds
2476*5e1b3bf7STakashi Iwai cptr = client_load_and_use_ptr(client);
24771da177e4SLinus Torvalds if (cptr == NULL)
24781da177e4SLinus Torvalds return -EINVAL;
24791da177e4SLinus Torvalds
24806b580f52STakashi Iwai if (!cptr->accept_output) {
24811da177e4SLinus Torvalds result = -EPERM;
24826b580f52STakashi Iwai } else { /* send it */
24836b580f52STakashi Iwai mutex_lock(&cptr->ioctl_mutex);
24847bd80091STakashi Iwai result = snd_seq_client_enqueue_event(cptr, ev, file, blocking,
24856b580f52STakashi Iwai false, 0,
24866b580f52STakashi Iwai &cptr->ioctl_mutex);
24876b580f52STakashi Iwai mutex_unlock(&cptr->ioctl_mutex);
24886b580f52STakashi Iwai }
24891da177e4SLinus Torvalds
24901da177e4SLinus Torvalds snd_seq_client_unlock(cptr);
24911da177e4SLinus Torvalds return result;
24921da177e4SLinus Torvalds }
249391715ed9STakashi Iwai EXPORT_SYMBOL(snd_seq_kernel_client_enqueue);
249491715ed9STakashi Iwai
24951da177e4SLinus Torvalds /*
24961da177e4SLinus Torvalds * exported, called by kernel clients to dispatch events directly to other
24971da177e4SLinus Torvalds * clients, bypassing the queues. Event time-stamp will be updated.
24981da177e4SLinus Torvalds *
24991da177e4SLinus Torvalds * RETURN VALUE: negative = delivery failed,
25001da177e4SLinus Torvalds * zero, or positive: the number of delivered events
25011da177e4SLinus Torvalds */
snd_seq_kernel_client_dispatch(int client,struct snd_seq_event * ev,int atomic,int hop)2502c7e0b5bfSTakashi Iwai int snd_seq_kernel_client_dispatch(int client, struct snd_seq_event * ev,
25031da177e4SLinus Torvalds int atomic, int hop)
25041da177e4SLinus Torvalds {
2505c7e0b5bfSTakashi Iwai struct snd_seq_client *cptr;
25061da177e4SLinus Torvalds int result;
25071da177e4SLinus Torvalds
25087eaa943cSTakashi Iwai if (snd_BUG_ON(!ev))
25097eaa943cSTakashi Iwai return -EINVAL;
25101da177e4SLinus Torvalds
25111da177e4SLinus Torvalds /* fill in client number */
25121da177e4SLinus Torvalds ev->queue = SNDRV_SEQ_QUEUE_DIRECT;
25131da177e4SLinus Torvalds ev->source.client = client;
25141da177e4SLinus Torvalds
25151da177e4SLinus Torvalds if (check_event_type_and_length(ev))
25161da177e4SLinus Torvalds return -EINVAL;
25171da177e4SLinus Torvalds
25181da177e4SLinus Torvalds cptr = snd_seq_client_use_ptr(client);
25191da177e4SLinus Torvalds if (cptr == NULL)
25201da177e4SLinus Torvalds return -EINVAL;
25211da177e4SLinus Torvalds
25221da177e4SLinus Torvalds if (!cptr->accept_output)
25231da177e4SLinus Torvalds result = -EPERM;
25241da177e4SLinus Torvalds else
25251da177e4SLinus Torvalds result = snd_seq_deliver_event(cptr, ev, atomic, hop);
25261da177e4SLinus Torvalds
25271da177e4SLinus Torvalds snd_seq_client_unlock(cptr);
25281da177e4SLinus Torvalds return result;
25291da177e4SLinus Torvalds }
253091715ed9STakashi Iwai EXPORT_SYMBOL(snd_seq_kernel_client_dispatch);
25311da177e4SLinus Torvalds
253277dfa8d3STakashi Sakamoto /**
253377dfa8d3STakashi Sakamoto * snd_seq_kernel_client_ctl - operate a command for a client with data in
253477dfa8d3STakashi Sakamoto * kernel space.
253577dfa8d3STakashi Sakamoto * @clientid: A numerical ID for a client.
253677dfa8d3STakashi Sakamoto * @cmd: An ioctl(2) command for ALSA sequencer operation.
253777dfa8d3STakashi Sakamoto * @arg: A pointer to data in kernel space.
253877dfa8d3STakashi Sakamoto *
253977dfa8d3STakashi Sakamoto * Against its name, both kernel/application client can be handled by this
254077dfa8d3STakashi Sakamoto * kernel API. A pointer of 'arg' argument should be in kernel space.
254177dfa8d3STakashi Sakamoto *
254277dfa8d3STakashi Sakamoto * Return: 0 at success. Negative error code at failure.
25431da177e4SLinus Torvalds */
snd_seq_kernel_client_ctl(int clientid,unsigned int cmd,void * arg)25441da177e4SLinus Torvalds int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg)
25451da177e4SLinus Torvalds {
25468ce8eb60STakashi Sakamoto const struct ioctl_handler *handler;
2547c7e0b5bfSTakashi Iwai struct snd_seq_client *client;
25481da177e4SLinus Torvalds
25491da177e4SLinus Torvalds client = clientptr(clientid);
25501da177e4SLinus Torvalds if (client == NULL)
25511da177e4SLinus Torvalds return -ENXIO;
25528ce8eb60STakashi Sakamoto
25538ce8eb60STakashi Sakamoto for (handler = ioctl_handlers; handler->cmd > 0; ++handler) {
25548ce8eb60STakashi Sakamoto if (handler->cmd == cmd)
25558ce8eb60STakashi Sakamoto return handler->func(client, arg);
25568ce8eb60STakashi Sakamoto }
25578ce8eb60STakashi Sakamoto
2558e12ec251STakashi Sakamoto pr_debug("ALSA: seq unknown ioctl() 0x%x (type='%c', number=0x%02x)\n",
2559e12ec251STakashi Sakamoto cmd, _IOC_TYPE(cmd), _IOC_NR(cmd));
2560e12ec251STakashi Sakamoto return -ENOTTY;
25611da177e4SLinus Torvalds }
256291715ed9STakashi Iwai EXPORT_SYMBOL(snd_seq_kernel_client_ctl);
25631da177e4SLinus Torvalds
25641da177e4SLinus Torvalds /* exported (for OSS emulator) */
snd_seq_kernel_client_write_poll(int clientid,struct file * file,poll_table * wait)25651da177e4SLinus Torvalds int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait)
25661da177e4SLinus Torvalds {
2567c7e0b5bfSTakashi Iwai struct snd_seq_client *client;
25681da177e4SLinus Torvalds
25691da177e4SLinus Torvalds client = clientptr(clientid);
25701da177e4SLinus Torvalds if (client == NULL)
25711da177e4SLinus Torvalds return -ENXIO;
25721da177e4SLinus Torvalds
25731da177e4SLinus Torvalds if (! snd_seq_write_pool_allocated(client))
25741da177e4SLinus Torvalds return 1;
25751da177e4SLinus Torvalds if (snd_seq_pool_poll_wait(client->pool, file, wait))
25761da177e4SLinus Torvalds return 1;
25771da177e4SLinus Torvalds return 0;
25781da177e4SLinus Torvalds }
257991715ed9STakashi Iwai EXPORT_SYMBOL(snd_seq_kernel_client_write_poll);
258091715ed9STakashi Iwai
2581d0c8308fSTakashi Iwai /* get a sequencer client object; for internal use from a kernel client */
snd_seq_kernel_client_get(int id)2582d0c8308fSTakashi Iwai struct snd_seq_client *snd_seq_kernel_client_get(int id)
2583d0c8308fSTakashi Iwai {
2584d0c8308fSTakashi Iwai return snd_seq_client_use_ptr(id);
2585d0c8308fSTakashi Iwai }
2586d0c8308fSTakashi Iwai EXPORT_SYMBOL_GPL(snd_seq_kernel_client_get);
2587d0c8308fSTakashi Iwai
2588d0c8308fSTakashi Iwai /* put a sequencer client object; for internal use from a kernel client */
snd_seq_kernel_client_put(struct snd_seq_client * cptr)2589d0c8308fSTakashi Iwai void snd_seq_kernel_client_put(struct snd_seq_client *cptr)
2590d0c8308fSTakashi Iwai {
2591d0c8308fSTakashi Iwai if (cptr)
2592d0c8308fSTakashi Iwai snd_seq_client_unlock(cptr);
2593d0c8308fSTakashi Iwai }
2594d0c8308fSTakashi Iwai EXPORT_SYMBOL_GPL(snd_seq_kernel_client_put);
2595d0c8308fSTakashi Iwai
25961da177e4SLinus Torvalds /*---------------------------------------------------------------------------*/
25971da177e4SLinus Torvalds
2598cd6a6503SJie Yang #ifdef CONFIG_SND_PROC_FS
25991da177e4SLinus Torvalds /*
26001da177e4SLinus Torvalds * /proc interface
26011da177e4SLinus Torvalds */
snd_seq_info_dump_subscribers(struct snd_info_buffer * buffer,struct snd_seq_port_subs_info * group,int is_src,char * msg)2602c7e0b5bfSTakashi Iwai static void snd_seq_info_dump_subscribers(struct snd_info_buffer *buffer,
2603c7e0b5bfSTakashi Iwai struct snd_seq_port_subs_info *group,
2604c7e0b5bfSTakashi Iwai int is_src, char *msg)
26051da177e4SLinus Torvalds {
26061da177e4SLinus Torvalds struct list_head *p;
2607c7e0b5bfSTakashi Iwai struct snd_seq_subscribers *s;
26081da177e4SLinus Torvalds int count = 0;
26091da177e4SLinus Torvalds
26101da177e4SLinus Torvalds down_read(&group->list_mutex);
26111da177e4SLinus Torvalds if (list_empty(&group->list_head)) {
26121da177e4SLinus Torvalds up_read(&group->list_mutex);
26131da177e4SLinus Torvalds return;
26141da177e4SLinus Torvalds }
26151da177e4SLinus Torvalds snd_iprintf(buffer, msg);
26161da177e4SLinus Torvalds list_for_each(p, &group->list_head) {
26171da177e4SLinus Torvalds if (is_src)
2618c7e0b5bfSTakashi Iwai s = list_entry(p, struct snd_seq_subscribers, src_list);
26191da177e4SLinus Torvalds else
2620c7e0b5bfSTakashi Iwai s = list_entry(p, struct snd_seq_subscribers, dest_list);
26211da177e4SLinus Torvalds if (count++)
26221da177e4SLinus Torvalds snd_iprintf(buffer, ", ");
26231da177e4SLinus Torvalds snd_iprintf(buffer, "%d:%d",
26241da177e4SLinus Torvalds is_src ? s->info.dest.client : s->info.sender.client,
26251da177e4SLinus Torvalds is_src ? s->info.dest.port : s->info.sender.port);
26261da177e4SLinus Torvalds if (s->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP)
26271da177e4SLinus Torvalds snd_iprintf(buffer, "[%c:%d]", ((s->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL) ? 'r' : 't'), s->info.queue);
26281da177e4SLinus Torvalds if (group->exclusive)
26291da177e4SLinus Torvalds snd_iprintf(buffer, "[ex]");
26301da177e4SLinus Torvalds }
26311da177e4SLinus Torvalds up_read(&group->list_mutex);
26321da177e4SLinus Torvalds snd_iprintf(buffer, "\n");
26331da177e4SLinus Torvalds }
26341da177e4SLinus Torvalds
26351da177e4SLinus Torvalds #define FLAG_PERM_RD(perm) ((perm) & SNDRV_SEQ_PORT_CAP_READ ? ((perm) & SNDRV_SEQ_PORT_CAP_SUBS_READ ? 'R' : 'r') : '-')
26361da177e4SLinus Torvalds #define FLAG_PERM_WR(perm) ((perm) & SNDRV_SEQ_PORT_CAP_WRITE ? ((perm) & SNDRV_SEQ_PORT_CAP_SUBS_WRITE ? 'W' : 'w') : '-')
26371da177e4SLinus Torvalds #define FLAG_PERM_EX(perm) ((perm) & SNDRV_SEQ_PORT_CAP_NO_EXPORT ? '-' : 'e')
26381da177e4SLinus Torvalds
26391da177e4SLinus Torvalds #define FLAG_PERM_DUPLEX(perm) ((perm) & SNDRV_SEQ_PORT_CAP_DUPLEX ? 'X' : '-')
26401da177e4SLinus Torvalds
port_direction_name(unsigned char dir)2641ff166a9dSTakashi Iwai static const char *port_direction_name(unsigned char dir)
2642ff166a9dSTakashi Iwai {
2643ff166a9dSTakashi Iwai static const char *names[4] = {
2644ff166a9dSTakashi Iwai "-", "In", "Out", "In/Out"
2645ff166a9dSTakashi Iwai };
2646ff166a9dSTakashi Iwai
2647ff166a9dSTakashi Iwai if (dir > SNDRV_SEQ_PORT_DIR_BIDIRECTION)
2648ff166a9dSTakashi Iwai return "Invalid";
2649ff166a9dSTakashi Iwai return names[dir];
2650ff166a9dSTakashi Iwai }
2651ff166a9dSTakashi Iwai
snd_seq_info_dump_ports(struct snd_info_buffer * buffer,struct snd_seq_client * client)2652c7e0b5bfSTakashi Iwai static void snd_seq_info_dump_ports(struct snd_info_buffer *buffer,
2653c7e0b5bfSTakashi Iwai struct snd_seq_client *client)
26541da177e4SLinus Torvalds {
26559244b2c3SJohannes Berg struct snd_seq_client_port *p;
26561da177e4SLinus Torvalds
26571a60d4c5SIngo Molnar mutex_lock(&client->ports_mutex);
26589244b2c3SJohannes Berg list_for_each_entry(p, &client->ports_list_head, list) {
265974661932STakashi Iwai if (p->capability & SNDRV_SEQ_PORT_CAP_INACTIVE)
266074661932STakashi Iwai continue;
2661ff166a9dSTakashi Iwai snd_iprintf(buffer, " Port %3d : \"%s\" (%c%c%c%c) [%s]\n",
26621da177e4SLinus Torvalds p->addr.port, p->name,
26631da177e4SLinus Torvalds FLAG_PERM_RD(p->capability),
26641da177e4SLinus Torvalds FLAG_PERM_WR(p->capability),
26651da177e4SLinus Torvalds FLAG_PERM_EX(p->capability),
2666ff166a9dSTakashi Iwai FLAG_PERM_DUPLEX(p->capability),
2667ff166a9dSTakashi Iwai port_direction_name(p->direction));
26681da177e4SLinus Torvalds snd_seq_info_dump_subscribers(buffer, &p->c_src, 1, " Connecting To: ");
26691da177e4SLinus Torvalds snd_seq_info_dump_subscribers(buffer, &p->c_dest, 0, " Connected From: ");
26701da177e4SLinus Torvalds }
26711a60d4c5SIngo Molnar mutex_unlock(&client->ports_mutex);
26721da177e4SLinus Torvalds }
26731da177e4SLinus Torvalds
midi_version_string(unsigned int version)267446397622STakashi Iwai static const char *midi_version_string(unsigned int version)
267546397622STakashi Iwai {
267646397622STakashi Iwai switch (version) {
267746397622STakashi Iwai case SNDRV_SEQ_CLIENT_LEGACY_MIDI:
267846397622STakashi Iwai return "Legacy";
267946397622STakashi Iwai case SNDRV_SEQ_CLIENT_UMP_MIDI_1_0:
268046397622STakashi Iwai return "UMP MIDI1";
268146397622STakashi Iwai case SNDRV_SEQ_CLIENT_UMP_MIDI_2_0:
268246397622STakashi Iwai return "UMP MIDI2";
268346397622STakashi Iwai default:
268446397622STakashi Iwai return "Unknown";
268546397622STakashi Iwai }
268646397622STakashi Iwai }
26871da177e4SLinus Torvalds
26881da177e4SLinus Torvalds /* exported to seq_info.c */
snd_seq_info_clients_read(struct snd_info_entry * entry,struct snd_info_buffer * buffer)2689c7e0b5bfSTakashi Iwai void snd_seq_info_clients_read(struct snd_info_entry *entry,
2690c7e0b5bfSTakashi Iwai struct snd_info_buffer *buffer)
26911da177e4SLinus Torvalds {
26921da177e4SLinus Torvalds int c;
2693c7e0b5bfSTakashi Iwai struct snd_seq_client *client;
26941da177e4SLinus Torvalds
26951da177e4SLinus Torvalds snd_iprintf(buffer, "Client info\n");
26961da177e4SLinus Torvalds snd_iprintf(buffer, " cur clients : %d\n", client_usage.cur);
26971da177e4SLinus Torvalds snd_iprintf(buffer, " peak clients : %d\n", client_usage.peak);
26981da177e4SLinus Torvalds snd_iprintf(buffer, " max clients : %d\n", SNDRV_SEQ_MAX_CLIENTS);
26991da177e4SLinus Torvalds snd_iprintf(buffer, "\n");
27001da177e4SLinus Torvalds
27011da177e4SLinus Torvalds /* list the client table */
27021da177e4SLinus Torvalds for (c = 0; c < SNDRV_SEQ_MAX_CLIENTS; c++) {
2703*5e1b3bf7STakashi Iwai client = client_load_and_use_ptr(c);
27041da177e4SLinus Torvalds if (client == NULL)
27051da177e4SLinus Torvalds continue;
27061da177e4SLinus Torvalds if (client->type == NO_CLIENT) {
27071da177e4SLinus Torvalds snd_seq_client_unlock(client);
27081da177e4SLinus Torvalds continue;
27091da177e4SLinus Torvalds }
27101da177e4SLinus Torvalds
271146397622STakashi Iwai snd_iprintf(buffer, "Client %3d : \"%s\" [%s %s]\n",
27121da177e4SLinus Torvalds c, client->name,
271346397622STakashi Iwai client->type == USER_CLIENT ? "User" : "Kernel",
271446397622STakashi Iwai midi_version_string(client->midi_version));
2715e85b9260STakashi Iwai #if IS_ENABLED(CONFIG_SND_SEQ_UMP)
2716e85b9260STakashi Iwai dump_ump_info(buffer, client);
2717e85b9260STakashi Iwai #endif
27181da177e4SLinus Torvalds snd_seq_info_dump_ports(buffer, client);
27191da177e4SLinus Torvalds if (snd_seq_write_pool_allocated(client)) {
27201da177e4SLinus Torvalds snd_iprintf(buffer, " Output pool :\n");
27211da177e4SLinus Torvalds snd_seq_info_pool(buffer, client->pool, " ");
27221da177e4SLinus Torvalds }
27231da177e4SLinus Torvalds if (client->type == USER_CLIENT && client->data.user.fifo &&
27241da177e4SLinus Torvalds client->data.user.fifo->pool) {
27251da177e4SLinus Torvalds snd_iprintf(buffer, " Input pool :\n");
27261da177e4SLinus Torvalds snd_seq_info_pool(buffer, client->data.user.fifo->pool, " ");
27271da177e4SLinus Torvalds }
27281da177e4SLinus Torvalds snd_seq_client_unlock(client);
27291da177e4SLinus Torvalds }
27301da177e4SLinus Torvalds }
2731cd6a6503SJie Yang #endif /* CONFIG_SND_PROC_FS */
27321da177e4SLinus Torvalds
27331da177e4SLinus Torvalds /*---------------------------------------------------------------------------*/
27341da177e4SLinus Torvalds
27351da177e4SLinus Torvalds
27361da177e4SLinus Torvalds /*
27371da177e4SLinus Torvalds * REGISTRATION PART
27381da177e4SLinus Torvalds */
27391da177e4SLinus Torvalds
27409c2e08c5SArjan van de Ven static const struct file_operations snd_seq_f_ops =
27411da177e4SLinus Torvalds {
27421da177e4SLinus Torvalds .owner = THIS_MODULE,
27431da177e4SLinus Torvalds .read = snd_seq_read,
27441da177e4SLinus Torvalds .write = snd_seq_write,
27451da177e4SLinus Torvalds .open = snd_seq_open,
27461da177e4SLinus Torvalds .release = snd_seq_release,
274702f4865fSTakashi Iwai .llseek = no_llseek,
27481da177e4SLinus Torvalds .poll = snd_seq_poll,
27491da177e4SLinus Torvalds .unlocked_ioctl = snd_seq_ioctl,
27501da177e4SLinus Torvalds .compat_ioctl = snd_seq_ioctl_compat,
27511da177e4SLinus Torvalds };
27521da177e4SLinus Torvalds
27532419891eSTakashi Iwai static struct device *seq_dev;
27545205388dSTakashi Iwai
27551da177e4SLinus Torvalds /*
27561da177e4SLinus Torvalds * register sequencer device
27571da177e4SLinus Torvalds */
snd_sequencer_device_init(void)27581da177e4SLinus Torvalds int __init snd_sequencer_device_init(void)
27591da177e4SLinus Torvalds {
27601da177e4SLinus Torvalds int err;
27611da177e4SLinus Torvalds
27622419891eSTakashi Iwai err = snd_device_alloc(&seq_dev, NULL);
27632419891eSTakashi Iwai if (err < 0)
27642419891eSTakashi Iwai return err;
27652419891eSTakashi Iwai dev_set_name(seq_dev, "seq");
27665205388dSTakashi Iwai
276704702e8dSTakashi Iwai mutex_lock(®ister_mutex);
276840a4b263STakashi Iwai err = snd_register_device(SNDRV_DEVICE_TYPE_SEQUENCER, NULL, 0,
27692419891eSTakashi Iwai &snd_seq_f_ops, NULL, seq_dev);
27701a60d4c5SIngo Molnar mutex_unlock(®ister_mutex);
277104702e8dSTakashi Iwai if (err < 0) {
27722419891eSTakashi Iwai put_device(seq_dev);
27731da177e4SLinus Torvalds return err;
27741da177e4SLinus Torvalds }
27751da177e4SLinus Torvalds
27761da177e4SLinus Torvalds return 0;
27771da177e4SLinus Torvalds }
27781da177e4SLinus Torvalds
27791da177e4SLinus Torvalds
27801da177e4SLinus Torvalds
27811da177e4SLinus Torvalds /*
27821da177e4SLinus Torvalds * unregister sequencer device
27831da177e4SLinus Torvalds */
snd_sequencer_device_done(void)278400976ad5STakashi Iwai void snd_sequencer_device_done(void)
27851da177e4SLinus Torvalds {
27862419891eSTakashi Iwai snd_unregister_device(seq_dev);
27872419891eSTakashi Iwai put_device(seq_dev);
27881da177e4SLinus Torvalds }
2789