linux/sound/core/seq/seq_clientmgr.c

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  ALSA sequencer Client Manager
 *  Copyright (c) 1998-2001 by Frank van de Pol <[email protected]>
 *                             Jaroslav Kysela <[email protected]>
 *                             Takashi Iwai <[email protected]>
 */

#include <linux/init.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <linux/kmod.h>

#include <sound/seq_kernel.h>
#include <sound/ump.h>
#include "seq_clientmgr.h"
#include "seq_memory.h"
#include "seq_queue.h"
#include "seq_timer.h"
#include "seq_info.h"
#include "seq_system.h"
#include "seq_ump_convert.h"
#include <sound/seq_device.h>
#ifdef CONFIG_COMPAT
#include <linux/compat.h>
#endif

/* Client Manager

 * this module handles the connections of userland and kernel clients
 * 
 */

/*
 * There are four ranges of client numbers (last two shared):
 * 0..15: global clients
 * 16..127: statically allocated client numbers for cards 0..27
 * 128..191: dynamically allocated client numbers for cards 28..31
 * 128..191: dynamically allocated client numbers for applications
 */

/* number of kernel non-card clients */
#define SNDRV_SEQ_GLOBAL_CLIENTS
/* clients per cards, for static clients */
#define SNDRV_SEQ_CLIENTS_PER_CARD
/* dynamically allocated client numbers (both kernel drivers and user space) */
#define SNDRV_SEQ_DYNAMIC_CLIENTS_BEGIN

#define SNDRV_SEQ_LFLG_INPUT
#define SNDRV_SEQ_LFLG_OUTPUT
#define SNDRV_SEQ_LFLG_OPEN

static DEFINE_SPINLOCK(clients_lock);
static DEFINE_MUTEX(register_mutex);

/*
 * client table
 */
static char clienttablock[SNDRV_SEQ_MAX_CLIENTS];
static struct snd_seq_client *clienttab[SNDRV_SEQ_MAX_CLIENTS];
static struct snd_seq_usage client_usage;

/*
 * prototypes
 */
static int bounce_error_event(struct snd_seq_client *client,
			      struct snd_seq_event *event,
			      int err, int atomic, int hop);
static int snd_seq_deliver_single_event(struct snd_seq_client *client,
					struct snd_seq_event *event,
					int atomic, int hop);

#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
static void free_ump_info(struct snd_seq_client *client);
#endif

/*
 */
static inline unsigned short snd_seq_file_flags(struct file *file)
{}

static inline int snd_seq_write_pool_allocated(struct snd_seq_client *client)
{}

/* return pointer to client structure for specified id */
static struct snd_seq_client *clientptr(int clientid)
{}

struct snd_seq_client *snd_seq_client_use_ptr(int clientid)
{}

/* Take refcount and perform ioctl_mutex lock on the given client;
 * used only for OSS sequencer
 * Unlock via snd_seq_client_ioctl_unlock() below
 */
bool snd_seq_client_ioctl_lock(int clientid)
{}
EXPORT_SYMBOL_GPL();

/* Unlock and unref the given client; for OSS sequencer use only */
void snd_seq_client_ioctl_unlock(int clientid)
{}
EXPORT_SYMBOL_GPL();

static void usage_alloc(struct snd_seq_usage *res, int num)
{}

static void usage_free(struct snd_seq_usage *res, int num)
{}

/* initialise data structures */
int __init client_init_data(void)
{}


static struct snd_seq_client *seq_create_client1(int client_index, int poolsize)
{}


static int seq_free_client1(struct snd_seq_client *client)
{}


static void seq_free_client(struct snd_seq_client * client)
{}



/* -------------------------------------------------------- */

/* create a user client */
static int snd_seq_open(struct inode *inode, struct file *file)
{}

/* delete a user client */
static int snd_seq_release(struct inode *inode, struct file *file)
{}

static bool event_is_compatible(const struct snd_seq_client *client,
				const struct snd_seq_event *ev)
{}

/* handle client read() */
/* possible error values:
 *	-ENXIO	invalid client or file open mode
 *	-ENOSPC	FIFO overflow (the flag is cleared after this error report)
 *	-EINVAL	no enough user-space buffer to write the whole event
 *	-EFAULT	seg. fault during copy to user space
 */
static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count,
			    loff_t *offset)
{}


/*
 * check access permission to the port
 */
static int check_port_perm(struct snd_seq_client_port *port, unsigned int flags)
{}

/*
 * check if the destination client is available, and return the pointer
 */
static struct snd_seq_client *get_event_dest_client(struct snd_seq_event *event)
{}


/*
 * Return the error event.
 *
 * If the receiver client is a user client, the original event is
 * encapsulated in SNDRV_SEQ_EVENT_BOUNCE as variable length event.  If
 * the original event is also variable length, the external data is
 * copied after the event record. 
 * If the receiver client is a kernel client, the original event is
 * quoted in SNDRV_SEQ_EVENT_KERNEL_ERROR, since this requires no extra
 * kmalloc.
 */
static int bounce_error_event(struct snd_seq_client *client,
			      struct snd_seq_event *event,
			      int err, int atomic, int hop)
{}


/*
 * rewrite the time-stamp of the event record with the curren time
 * of the given queue.
 * return non-zero if updated.
 */
static int update_timestamp_of_queue(struct snd_seq_event *event,
				     int queue, int real_time)
{}

/* deliver a single event; called from below and UMP converter */
int __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)
{}

/*
 * deliver an event to the specified destination.
 * if filter is non-zero, client filter bitmap is tested.
 *
 *  RETURN VALUE: 0 : if succeeded
 *		 <0 : error
 */
static int snd_seq_deliver_single_event(struct snd_seq_client *client,
					struct snd_seq_event *event,
					int atomic, int hop)
{}


/*
 * send the event to all subscribers:
 */
static int __deliver_to_subscribers(struct snd_seq_client *client,
				    struct snd_seq_event *event,
				    struct snd_seq_client_port *src_port,
				    int atomic, int hop)
{}

static int deliver_to_subscribers(struct snd_seq_client *client,
				  struct snd_seq_event *event,
				  int atomic, int hop)
{}

/* deliver an event to the destination port(s).
 * if the event is to subscribers or broadcast, the event is dispatched
 * to multiple targets.
 *
 * RETURN VALUE: n > 0  : the number of delivered events.
 *               n == 0 : the event was not passed to any client.
 *               n < 0  : error - event was not processed.
 */
static int snd_seq_deliver_event(struct snd_seq_client *client, struct snd_seq_event *event,
				 int atomic, int hop)
{}

/*
 * dispatch an event cell:
 * This function is called only from queue check routines in timer
 * interrupts or after enqueued.
 * The event cell shall be released or re-queued in this function.
 *
 * RETURN VALUE: n > 0  : the number of delivered events.
 *		 n == 0 : the event was not passed to any client.
 *		 n < 0  : error - event was not processed.
 */
int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop)
{}


/* Allocate a cell from client pool and enqueue it to queue:
 * if pool is empty and blocking is TRUE, sleep until a new cell is
 * available.
 */
static int 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)
{}


/*
 * check validity of event type and data length.
 * return non-zero if invalid.
 */
static int check_event_type_and_length(struct snd_seq_event *ev)
{}


/* handle write() */
/* possible error values:
 *	-ENXIO	invalid client or file open mode
 *	-ENOMEM	malloc failed
 *	-EFAULT	seg. fault during copy from user space
 *	-EINVAL	invalid event
 *	-EAGAIN	no space in output pool
 *	-EINTR	interrupts while sleep
 *	-EMLINK	too many hops
 *	others	depends on return value from driver callback
 */
static ssize_t snd_seq_write(struct file *file, const char __user *buf,
			     size_t count, loff_t *offset)
{}


/*
 * handle polling
 */
static __poll_t snd_seq_poll(struct file *file, poll_table * wait)
{}


/*-----------------------------------------------------*/

static int snd_seq_ioctl_pversion(struct snd_seq_client *client, void *arg)
{}

static int snd_seq_ioctl_user_pversion(struct snd_seq_client *client, void *arg)
{}

static int snd_seq_ioctl_client_id(struct snd_seq_client *client, void *arg)
{}

/* SYSTEM_INFO ioctl() */
static int snd_seq_ioctl_system_info(struct snd_seq_client *client, void *arg)
{}


/* RUNNING_MODE ioctl() */
static int snd_seq_ioctl_running_mode(struct snd_seq_client *client, void  *arg)
{}

/* CLIENT_INFO ioctl() */
static void get_client_info(struct snd_seq_client *cptr,
			    struct snd_seq_client_info *info)
{}

static int snd_seq_ioctl_get_client_info(struct snd_seq_client *client,
					 void *arg)
{}


/* CLIENT_INFO ioctl() */
static int snd_seq_ioctl_set_client_info(struct snd_seq_client *client,
					 void *arg)
{}


/* 
 * CREATE PORT ioctl() 
 */
static int snd_seq_ioctl_create_port(struct snd_seq_client *client, void *arg)
{}

/* 
 * DELETE PORT ioctl() 
 */
static int snd_seq_ioctl_delete_port(struct snd_seq_client *client, void *arg)
{}


/* 
 * GET_PORT_INFO ioctl() (on any client) 
 */
static int snd_seq_ioctl_get_port_info(struct snd_seq_client *client, void *arg)
{}


/* 
 * SET_PORT_INFO ioctl() (only ports on this/own client) 
 */
static int snd_seq_ioctl_set_port_info(struct snd_seq_client *client, void *arg)
{}


/*
 * port subscription (connection)
 */
#define PERM_RD
#define PERM_WR

static int 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)
{}

/*
 * send an subscription notify event to user client:
 * client must be user client.
 */
int snd_seq_client_notify_subscription(int client, int port,
				       struct snd_seq_port_subscribe *info,
				       int evtype)
{}


/* 
 * add to port's subscription list IOCTL interface 
 */
static int snd_seq_ioctl_subscribe_port(struct snd_seq_client *client,
					void *arg)
{}


/* 
 * remove from port's subscription list 
 */
static int snd_seq_ioctl_unsubscribe_port(struct snd_seq_client *client,
					  void *arg)
{}


/* CREATE_QUEUE ioctl() */
static int snd_seq_ioctl_create_queue(struct snd_seq_client *client, void *arg)
{}

/* DELETE_QUEUE ioctl() */
static int snd_seq_ioctl_delete_queue(struct snd_seq_client *client, void *arg)
{}

/* GET_QUEUE_INFO ioctl() */
static int snd_seq_ioctl_get_queue_info(struct snd_seq_client *client,
					void *arg)
{}

/* SET_QUEUE_INFO ioctl() */
static int snd_seq_ioctl_set_queue_info(struct snd_seq_client *client,
					void *arg)
{}

/* GET_NAMED_QUEUE ioctl() */
static int snd_seq_ioctl_get_named_queue(struct snd_seq_client *client,
					 void *arg)
{}

/* GET_QUEUE_STATUS ioctl() */
static int snd_seq_ioctl_get_queue_status(struct snd_seq_client *client,
					  void *arg)
{}


/* GET_QUEUE_TEMPO ioctl() */
static int snd_seq_ioctl_get_queue_tempo(struct snd_seq_client *client,
					 void *arg)
{}


/* SET_QUEUE_TEMPO ioctl() */
int snd_seq_set_queue_tempo(int client, struct snd_seq_queue_tempo *tempo)
{}
EXPORT_SYMBOL();

static int snd_seq_ioctl_set_queue_tempo(struct snd_seq_client *client,
					 void *arg)
{}


/* GET_QUEUE_TIMER ioctl() */
static int snd_seq_ioctl_get_queue_timer(struct snd_seq_client *client,
					 void *arg)
{}


/* SET_QUEUE_TIMER ioctl() */
static int snd_seq_ioctl_set_queue_timer(struct snd_seq_client *client,
					 void *arg)
{}


/* GET_QUEUE_CLIENT ioctl() */
static int snd_seq_ioctl_get_queue_client(struct snd_seq_client *client,
					  void *arg)
{}


/* SET_QUEUE_CLIENT ioctl() */
static int snd_seq_ioctl_set_queue_client(struct snd_seq_client *client,
					  void *arg)
{}


/* GET_CLIENT_POOL ioctl() */
static int snd_seq_ioctl_get_client_pool(struct snd_seq_client *client,
					 void *arg)
{}

/* SET_CLIENT_POOL ioctl() */
static int snd_seq_ioctl_set_client_pool(struct snd_seq_client *client,
					 void *arg)
{}


/* REMOVE_EVENTS ioctl() */
static int snd_seq_ioctl_remove_events(struct snd_seq_client *client,
				       void *arg)
{}


/*
 * get subscription info
 */
static int snd_seq_ioctl_get_subscription(struct snd_seq_client *client,
					  void *arg)
{}


/*
 * get subscription info - check only its presence
 */
static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, void *arg)
{}


/*
 * query next client
 */
static int snd_seq_ioctl_query_next_client(struct snd_seq_client *client,
					   void *arg)
{}

/* 
 * query next port
 */
static int snd_seq_ioctl_query_next_port(struct snd_seq_client *client,
					 void *arg)
{}

#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
#define NUM_UMP_INFOS

static void free_ump_info(struct snd_seq_client *client)
{}

static void terminate_ump_info_strings(void *p, int type)
{}

#ifdef CONFIG_SND_PROC_FS
static void dump_ump_info(struct snd_info_buffer *buffer,
			  struct snd_seq_client *client)
{}
#endif

/* UMP-specific ioctls -- called directly without data copy */
static int snd_seq_ioctl_client_ump_info(struct snd_seq_client *caller,
					 unsigned int cmd,
					 unsigned long arg)
{}
#endif

/* -------------------------------------------------------- */

static const struct ioctl_handler {} ioctl_handlers[] =;

static long snd_seq_ioctl(struct file *file, unsigned int cmd,
			  unsigned long arg)
{}

#ifdef CONFIG_COMPAT
#include "seq_compat.c"
#else
#define snd_seq_ioctl_compat
#endif

/* -------------------------------------------------------- */


/* exported to kernel modules */
int snd_seq_create_kernel_client(struct snd_card *card, int client_index,
				 const char *name_fmt, ...)
{}
EXPORT_SYMBOL();

/* exported to kernel modules */
int snd_seq_delete_kernel_client(int client)
{}
EXPORT_SYMBOL();

/*
 * exported, called by kernel clients to enqueue events (w/o blocking)
 *
 * RETURN VALUE: zero if succeed, negative if error
 */
int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event *ev,
				  struct file *file, bool blocking)
{}
EXPORT_SYMBOL();

/* 
 * exported, called by kernel clients to dispatch events directly to other
 * clients, bypassing the queues.  Event time-stamp will be updated.
 *
 * RETURN VALUE: negative = delivery failed,
 *		 zero, or positive: the number of delivered events
 */
int snd_seq_kernel_client_dispatch(int client, struct snd_seq_event * ev,
				   int atomic, int hop)
{}
EXPORT_SYMBOL();

/**
 * snd_seq_kernel_client_ctl - operate a command for a client with data in
 *			       kernel space.
 * @clientid:	A numerical ID for a client.
 * @cmd:	An ioctl(2) command for ALSA sequencer operation.
 * @arg:	A pointer to data in kernel space.
 *
 * Against its name, both kernel/application client can be handled by this
 * kernel API. A pointer of 'arg' argument should be in kernel space.
 *
 * Return: 0 at success. Negative error code at failure.
 */
int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg)
{}
EXPORT_SYMBOL();

/* exported (for OSS emulator) */
int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait)
{}
EXPORT_SYMBOL();

/* get a sequencer client object; for internal use from a kernel client */
struct snd_seq_client *snd_seq_kernel_client_get(int id)
{}
EXPORT_SYMBOL_GPL();

/* put a sequencer client object; for internal use from a kernel client */
void snd_seq_kernel_client_put(struct snd_seq_client *cptr)
{}
EXPORT_SYMBOL_GPL();

/*---------------------------------------------------------------------------*/

#ifdef CONFIG_SND_PROC_FS
/*
 *  /proc interface
 */
static void snd_seq_info_dump_subscribers(struct snd_info_buffer *buffer,
					  struct snd_seq_port_subs_info *group,
					  int is_src, char *msg)
{}

#define FLAG_PERM_RD(perm)
#define FLAG_PERM_WR(perm)
#define FLAG_PERM_EX(perm)

#define FLAG_PERM_DUPLEX(perm)

static const char *port_direction_name(unsigned char dir)
{}

static void snd_seq_info_dump_ports(struct snd_info_buffer *buffer,
				    struct snd_seq_client *client)
{}

static const char *midi_version_string(unsigned int version)
{}

/* exported to seq_info.c */
void snd_seq_info_clients_read(struct snd_info_entry *entry, 
			       struct snd_info_buffer *buffer)
{}
#endif /* CONFIG_SND_PROC_FS */

/*---------------------------------------------------------------------------*/


/*
 *  REGISTRATION PART
 */

static const struct file_operations snd_seq_f_ops =;

static struct device *seq_dev;

/* 
 * register sequencer device 
 */
int __init snd_sequencer_device_init(void)
{}



/* 
 * unregister sequencer device 
 */
void snd_sequencer_device_done(void)
{}