⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 seq_clientmgr.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *  ALSA sequencer Client Manager *  Copyright (c) 1998-2001 by Frank van de Pol <fvdpol@coil.demon.nl> *                             Jaroslav Kysela <perex@suse.cz> *                             Takashi Iwai <tiwai@suse.de> * * *   This program is free software; you can redistribute it and/or modify *   it under the terms of the GNU General Public License as published by *   the Free Software Foundation; either version 2 of the License, or *   (at your option) any later version. * *   This program is distributed in the hope that it will be useful, *   but WITHOUT ANY WARRANTY; without even the implied warranty of *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *   GNU General Public License for more details. * *   You should have received a copy of the GNU General Public License *   along with this program; if not, write to the Free Software *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA * */#include <sound/driver.h>#include <linux/init.h>#include <linux/smp_lock.h>#include <linux/slab.h>#include <sound/core.h>#include <sound/minors.h>#include <linux/kmod.h>#include <sound/seq_kernel.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 <sound/seq_device.h>#ifdef CONFIG_COMPAT#include <linux/compat.h>#endif/* Client Manager * this module handles the connections of userland and kernel clients *  */#define SNDRV_SEQ_LFLG_INPUT	0x0001#define SNDRV_SEQ_LFLG_OUTPUT	0x0002#define SNDRV_SEQ_LFLG_OPEN	(SNDRV_SEQ_LFLG_INPUT|SNDRV_SEQ_LFLG_OUTPUT)static DEFINE_SPINLOCK(clients_lock);static DECLARE_MUTEX(register_mutex);/* * client table */static char clienttablock[SNDRV_SEQ_MAX_CLIENTS];static client_t *clienttab[SNDRV_SEQ_MAX_CLIENTS];static usage_t client_usage;/* * prototypes */static int bounce_error_event(client_t *client, snd_seq_event_t *event, int err, int atomic, int hop);static int snd_seq_deliver_single_event(client_t *client, snd_seq_event_t *event, int filter, int atomic, int hop);/* */ static inline mm_segment_t snd_enter_user(void){	mm_segment_t fs = get_fs();	set_fs(get_ds());	return fs;}static inline void snd_leave_user(mm_segment_t fs){	set_fs(fs);}/* */static inline unsigned short snd_seq_file_flags(struct file *file){        switch (file->f_mode & (FMODE_READ | FMODE_WRITE)) {        case FMODE_WRITE:                return SNDRV_SEQ_LFLG_OUTPUT;        case FMODE_READ:                return SNDRV_SEQ_LFLG_INPUT;        default:                return SNDRV_SEQ_LFLG_OPEN;        }}static inline int snd_seq_write_pool_allocated(client_t *client){	return snd_seq_total_cells(client->pool) > 0;}/* return pointer to client structure for specified id */static client_t *clientptr(int clientid){	if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) {		snd_printd("Seq: oops. Trying to get pointer to client %d\n", clientid);		return NULL;	}	return clienttab[clientid];}extern int seq_client_load[];client_t *snd_seq_client_use_ptr(int clientid){	unsigned long flags;	client_t *client;	if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) {		snd_printd("Seq: oops. Trying to get pointer to client %d\n", clientid);		return NULL;	}	spin_lock_irqsave(&clients_lock, flags);	client = clientptr(clientid);	if (client)		goto __lock;	if (clienttablock[clientid]) {		spin_unlock_irqrestore(&clients_lock, flags);		return NULL;	}	spin_unlock_irqrestore(&clients_lock, flags);#ifdef CONFIG_KMOD	if (!in_interrupt() && current->fs->root) {		static char client_requested[64];		static char card_requested[SNDRV_CARDS];		if (clientid < 64) {			int idx;						if (! client_requested[clientid] && current->fs->root) {				client_requested[clientid] = 1;				for (idx = 0; idx < 64; idx++) {					if (seq_client_load[idx] < 0)						break;					if (seq_client_load[idx] == clientid) {						request_module("snd-seq-client-%i", clientid);						break;					}				}			}		} else if (clientid >= 64 && clientid < 128) {			int card = (clientid - 64) / 8;			if (card < snd_ecards_limit) {				if (! card_requested[card]) {					card_requested[card] = 1;					snd_request_card(card);				}				snd_seq_device_load_drivers();			}		}		spin_lock_irqsave(&clients_lock, flags);		client = clientptr(clientid);		if (client)			goto __lock;		spin_unlock_irqrestore(&clients_lock, flags);	}#endif	return NULL;      __lock:	snd_use_lock_use(&client->use_lock);	spin_unlock_irqrestore(&clients_lock, flags);	return client;}static void usage_alloc(usage_t * res, int num){	res->cur += num;	if (res->cur > res->peak)		res->peak = res->cur;}static void usage_free(usage_t * res, int num){	res->cur -= num;}/* initialise data structures */int __init client_init_data(void){	/* zap out the client table */	memset(&clienttablock, 0, sizeof(clienttablock));	memset(&clienttab, 0, sizeof(clienttab));	return 0;}static client_t *seq_create_client1(int client_index, int poolsize){	unsigned long flags;	int c;	client_t *client;	/* init client data */	client = kzalloc(sizeof(*client), GFP_KERNEL);	if (client == NULL)		return NULL;	client->pool = snd_seq_pool_new(poolsize);	if (client->pool == NULL) {		kfree(client);		return NULL;	}	client->type = NO_CLIENT;	snd_use_lock_init(&client->use_lock);	rwlock_init(&client->ports_lock);	init_MUTEX(&client->ports_mutex);	INIT_LIST_HEAD(&client->ports_list_head);	/* find free slot in the client table */	spin_lock_irqsave(&clients_lock, flags);	if (client_index < 0) {		for (c = 128; c < SNDRV_SEQ_MAX_CLIENTS; c++) {			if (clienttab[c] || clienttablock[c])				continue;			clienttab[client->number = c] = client;			spin_unlock_irqrestore(&clients_lock, flags);			return client;		}	} else {		if (clienttab[client_index] == NULL && !clienttablock[client_index]) {			clienttab[client->number = client_index] = client;			spin_unlock_irqrestore(&clients_lock, flags);			return client;		}	}	spin_unlock_irqrestore(&clients_lock, flags);	snd_seq_pool_delete(&client->pool);	kfree(client);	return NULL;	/* no free slot found or busy, return failure code */}static int seq_free_client1(client_t *client){	unsigned long flags;	snd_assert(client != NULL, return -EINVAL);	snd_seq_delete_all_ports(client);	snd_seq_queue_client_leave(client->number);	spin_lock_irqsave(&clients_lock, flags);	clienttablock[client->number] = 1;	clienttab[client->number] = NULL;	spin_unlock_irqrestore(&clients_lock, flags);	snd_use_lock_sync(&client->use_lock);	snd_seq_queue_client_termination(client->number);	if (client->pool)		snd_seq_pool_delete(&client->pool);	spin_lock_irqsave(&clients_lock, flags);	clienttablock[client->number] = 0;	spin_unlock_irqrestore(&clients_lock, flags);	return 0;}static void seq_free_client(client_t * client){	down(&register_mutex);	switch (client->type) {	case NO_CLIENT:		snd_printk(KERN_WARNING "Seq: Trying to free unused client %d\n", client->number);		break;	case USER_CLIENT:	case KERNEL_CLIENT:		seq_free_client1(client);		usage_free(&client_usage, 1);		break;	default:		snd_printk(KERN_ERR "Seq: Trying to free client %d with undefined type = %d\n", client->number, client->type);	}	up(&register_mutex);	snd_seq_system_client_ev_client_exit(client->number);}/* -------------------------------------------------------- *//* create a user client */static int snd_seq_open(struct inode *inode, struct file *file){	int c, mode;			/* client id */	client_t *client;	user_client_t *user;	if (down_interruptible(&register_mutex))		return -ERESTARTSYS;	client = seq_create_client1(-1, SNDRV_SEQ_DEFAULT_EVENTS);	if (client == NULL) {		up(&register_mutex);		return -ENOMEM;	/* failure code */	}	mode = snd_seq_file_flags(file);	if (mode & SNDRV_SEQ_LFLG_INPUT)		client->accept_input = 1;	if (mode & SNDRV_SEQ_LFLG_OUTPUT)		client->accept_output = 1;	user = &client->data.user;	user->fifo = NULL;	user->fifo_pool_size = 0;	if (mode & SNDRV_SEQ_LFLG_INPUT) {		user->fifo_pool_size = SNDRV_SEQ_DEFAULT_CLIENT_EVENTS;		user->fifo = snd_seq_fifo_new(user->fifo_pool_size);		if (user->fifo == NULL) {			seq_free_client1(client);			kfree(client);			up(&register_mutex);			return -ENOMEM;		}	}	usage_alloc(&client_usage, 1);	client->type = USER_CLIENT;	up(&register_mutex);	c = client->number;	file->private_data = client;	/* fill client data */	user->file = file;	sprintf(client->name, "Client-%d", c);	/* make others aware this new client */	snd_seq_system_client_ev_client_start(c);	return 0;}/* delete a user client */static int snd_seq_release(struct inode *inode, struct file *file){	client_t *client = (client_t *) file->private_data;	if (client) {		seq_free_client(client);		if (client->data.user.fifo)			snd_seq_fifo_delete(&client->data.user.fifo);		kfree(client);	}	return 0;}/* 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){	client_t *client = (client_t *) file->private_data;	fifo_t *fifo;	int err;	long result = 0;	snd_seq_event_cell_t *cell;	if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT))		return -ENXIO;	if (!access_ok(VERIFY_WRITE, buf, count))		return -EFAULT;	/* check client structures are in place */	snd_assert(client != NULL, return -ENXIO);	if (!client->accept_input || (fifo = client->data.user.fifo) == NULL)		return -ENXIO;	if (atomic_read(&fifo->overflow) > 0) {		/* buffer overflow is detected */		snd_seq_fifo_clear(fifo);		/* return error code */		return -ENOSPC;	}	cell = NULL;	err = 0;	snd_seq_fifo_lock(fifo);	/* while data available in queue */	while (count >= sizeof(snd_seq_event_t)) {		int nonblock;		nonblock = (file->f_flags & O_NONBLOCK) || result > 0;		if ((err = snd_seq_fifo_cell_out(fifo, &cell, nonblock)) < 0) {			break;		}		if (snd_seq_ev_is_variable(&cell->event)) {			snd_seq_event_t tmpev;			tmpev = cell->event;			tmpev.data.ext.len &= ~SNDRV_SEQ_EXT_MASK;			if (copy_to_user(buf, &tmpev, sizeof(snd_seq_event_t))) {				err = -EFAULT;				break;			}			count -= sizeof(snd_seq_event_t);			buf += sizeof(snd_seq_event_t);			err = snd_seq_expand_var_event(&cell->event, count,						       (char __force *)buf, 0,						       sizeof(snd_seq_event_t));			if (err < 0)				break;			result += err;			count -= err;			buf += err;		} else {			if (copy_to_user(buf, &cell->event, sizeof(snd_seq_event_t))) {				err = -EFAULT;				break;			}			count -= sizeof(snd_seq_event_t);			buf += sizeof(snd_seq_event_t);		}		snd_seq_cell_free(cell);		cell = NULL; /* to be sure */		result += sizeof(snd_seq_event_t);	}	if (err < 0) {		if (cell)			snd_seq_fifo_cell_putback(fifo, cell);		if (err == -EAGAIN && result > 0)			err = 0;	}	snd_seq_fifo_unlock(fifo);	return (err < 0) ? err : result;}/* * check access permission to the port */static int check_port_perm(client_port_t *port, unsigned int flags){	if ((port->capability & flags) != flags)		return 0;	return flags;}/* * check if the destination client is available, and return the pointer * if filter is non-zero, client filter bitmap is tested. */static client_t *get_event_dest_client(snd_seq_event_t *event, int filter){	client_t *dest;	dest = snd_seq_client_use_ptr(event->dest.client);	if (dest == NULL)		return NULL;	if (! dest->accept_input)		goto __not_avail;	if ((dest->filter & SNDRV_SEQ_FILTER_USE_EVENT) &&	    ! test_bit(event->type, dest->event_filter))		goto __not_avail;	if (filter && !(dest->filter & filter))		goto __not_avail;	return dest; /* ok - accessible */__not_avail:	snd_seq_client_unlock(dest);	return NULL;}/* * 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(client_t *client, snd_seq_event_t *event,			      int err, int atomic, int hop){	snd_seq_event_t bounce_ev;	int result;	if (client == NULL ||

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -