xref: /openbmc/linux/sound/core/seq/seq_clientmgr.c (revision 8ebc80a25f9d9bf7a8e368b266d5b740c485c362)
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(&register_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(&register_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(&register_mutex);
350aa1e77e6SClemens Ladisch 	client = seq_create_client1(-1, SNDRV_SEQ_DEFAULT_EVENTS);
35104702e8dSTakashi Iwai 	if (!client) {
3521a60d4c5SIngo Molnar 		mutex_unlock(&register_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(&register_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(&register_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(&register_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(&register_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(&register_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(&register_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(&register_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