rawmidi.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,674 行 · 第 1/4 页

C
1,674
字号
/* *  Abstract layer for MIDI v1.0 stream *  Copyright (c) by Jaroslav Kysela <perex@suse.cz> * * *   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 <sound/core.h>#include <linux/major.h>#include <linux/init.h>#include <linux/smp_lock.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/time.h>#include <linux/wait.h>#include <linux/moduleparam.h>#include <sound/rawmidi.h>#include <sound/info.h>#include <sound/control.h>#include <sound/minors.h>#include <sound/initval.h>MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");MODULE_DESCRIPTION("Midlevel RawMidi code for ALSA.");MODULE_LICENSE("GPL");#ifdef CONFIG_SND_OSSEMULstatic int midi_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 0};static int amidi_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};static int boot_devs;module_param_array(midi_map, int, boot_devs, 0444);MODULE_PARM_DESC(midi_map, "Raw MIDI device number assigned to 1st OSS device.");module_param_array(amidi_map, int, boot_devs, 0444);MODULE_PARM_DESC(amidi_map, "Raw MIDI device number assigned to 2nd OSS device.");#endif /* CONFIG_SND_OSSEMUL */static int snd_rawmidi_free(snd_rawmidi_t *rawmidi);static int snd_rawmidi_dev_free(snd_device_t *device);static int snd_rawmidi_dev_register(snd_device_t *device);static int snd_rawmidi_dev_disconnect(snd_device_t *device);static int snd_rawmidi_dev_unregister(snd_device_t *device);snd_rawmidi_t *snd_rawmidi_devices[SNDRV_CARDS * SNDRV_RAWMIDI_DEVICES];static DECLARE_MUTEX(register_mutex);static inline unsigned short snd_rawmidi_file_flags(struct file *file){	switch (file->f_mode & (FMODE_READ | FMODE_WRITE)) {	case FMODE_WRITE:		return SNDRV_RAWMIDI_LFLG_OUTPUT;	case FMODE_READ:		return SNDRV_RAWMIDI_LFLG_INPUT;	default:		return SNDRV_RAWMIDI_LFLG_OPEN;	}}static inline int snd_rawmidi_ready(snd_rawmidi_substream_t * substream){	snd_rawmidi_runtime_t *runtime = substream->runtime;	return runtime->avail >= runtime->avail_min;}static inline int snd_rawmidi_ready_append(snd_rawmidi_substream_t * substream, size_t count){	snd_rawmidi_runtime_t *runtime = substream->runtime;	return runtime->avail >= runtime->avail_min &&	       (!substream->append || runtime->avail >= count);}static int snd_rawmidi_init(snd_rawmidi_substream_t *substream){	snd_rawmidi_runtime_t *runtime = substream->runtime;	spin_lock_init(&runtime->lock);	init_waitqueue_head(&runtime->sleep);	runtime->event = NULL;	runtime->buffer_size = PAGE_SIZE;	runtime->avail_min = 1;	if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT)		runtime->avail = 0;	else		runtime->avail = runtime->buffer_size;	if ((runtime->buffer = kmalloc(runtime->buffer_size, GFP_KERNEL)) == NULL)		return -ENOMEM;	runtime->appl_ptr = runtime->hw_ptr = 0;	return 0;}static int snd_rawmidi_done_buffer(snd_rawmidi_runtime_t *runtime){	if (runtime->buffer) {		kfree(runtime->buffer);		runtime->buffer = NULL;	}	return 0;}int snd_rawmidi_drop_output(snd_rawmidi_substream_t * substream){	snd_rawmidi_runtime_t *runtime = substream->runtime;	substream->ops->trigger(substream, 0);	runtime->trigger = 0;	runtime->drain = 0;	/* interrupts are not enabled at this moment,	   so spinlock is not required */	runtime->appl_ptr = runtime->hw_ptr = 0;	runtime->avail = runtime->buffer_size;	return 0;}int snd_rawmidi_drain_output(snd_rawmidi_substream_t * substream){	int err;	long timeout;	snd_rawmidi_runtime_t *runtime = substream->runtime;	err = 0;	runtime->drain = 1;	while (runtime->avail < runtime->buffer_size) {		timeout = interruptible_sleep_on_timeout(&runtime->sleep, 10 * HZ);		if (signal_pending(current)) {			err = -ERESTARTSYS;			break;		}		if (runtime->avail < runtime->buffer_size && !timeout) {			snd_printk(KERN_WARNING "rawmidi drain error (avail = %li, buffer_size = %li)\n", (long)runtime->avail, (long)runtime->buffer_size);			err = -EIO;			break;		}	}	runtime->drain = 0;	if (err != -ERESTARTSYS) {		/* we need wait a while to make sure that Tx FIFOs are empty */		if (substream->ops->drain)			substream->ops->drain(substream);		else {			set_current_state(TASK_UNINTERRUPTIBLE);			schedule_timeout(HZ / 20);		}		snd_rawmidi_drop_output(substream);	}	return err;}int snd_rawmidi_drain_input(snd_rawmidi_substream_t * substream){	snd_rawmidi_runtime_t *runtime = substream->runtime;	substream->ops->trigger(substream, 0);	runtime->trigger = 0;	runtime->drain = 0;	/* interrupts aren't enabled at this moment, so spinlock isn't needed */	runtime->appl_ptr = runtime->hw_ptr = 0;	runtime->avail = 0;	return 0;}int snd_rawmidi_kernel_open(int cardnum, int device, int subdevice,			    int mode, snd_rawmidi_file_t * rfile){	snd_rawmidi_t *rmidi;	struct list_head *list1, *list2;	snd_rawmidi_substream_t *sinput, *soutput;	snd_rawmidi_runtime_t *input = NULL, *output = NULL;	int err;	if (rfile)		rfile->input = rfile->output = NULL;	rmidi = snd_rawmidi_devices[(cardnum * SNDRV_RAWMIDI_DEVICES) + device];	if (rmidi == NULL) {		err = -ENODEV;		goto __error1;	}	if (!try_module_get(rmidi->card->module)) {		err = -EFAULT;		goto __error1;	}	if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK))		down(&rmidi->open_mutex);	if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {		if (!(rmidi->info_flags & SNDRV_RAWMIDI_INFO_INPUT)) {			err = -ENXIO;			goto __error;		}		if (subdevice >= 0 && (unsigned int)subdevice >= rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_count) {			err = -ENODEV;			goto __error;		}		if (rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened >=		    rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_count) {			err = -EAGAIN;			goto __error;		}	}	if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {		if (!(rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT)) {			err = -ENXIO;			goto __error;		}		if (subdevice >= 0 && (unsigned int)subdevice >= rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_count) {			err = -ENODEV;			goto __error;		}		if (rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened >=		    rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_count) {			err = -EAGAIN;			goto __error;		}	}	list1 = rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams.next;	while (1) {		if (list1 == &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) {			sinput = NULL;			if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {				err = -EAGAIN;				goto __error;			}			break;		}		sinput = list_entry(list1, snd_rawmidi_substream_t, list);		if ((mode & SNDRV_RAWMIDI_LFLG_INPUT) && sinput->opened)			goto __nexti;		if (subdevice < 0 || (subdevice >= 0 && subdevice == sinput->number))			break;	      __nexti:		list1 = list1->next;	}	list2 = rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams.next;	while (1) {		if (list2 == &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) {			soutput = NULL;			if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {				err = -EAGAIN;				goto __error;			}			break;		}		soutput = list_entry(list2, snd_rawmidi_substream_t, list);		if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {			if (mode & SNDRV_RAWMIDI_LFLG_APPEND) {				if (soutput->opened && !soutput->append)					goto __nexto;			} else {				if (soutput->opened)					goto __nexto;			}		}		if (subdevice < 0 || (subdevice >= 0 && subdevice == soutput->number))			break;	      __nexto:		list2 = list2->next;	}	if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {		input = kcalloc(1, sizeof(*input), GFP_KERNEL);		if (input == NULL) {			err = -ENOMEM;			goto __error;		}		sinput->runtime = input;		if (snd_rawmidi_init(sinput) < 0) {			err = -ENOMEM;			goto __error;		}		if ((err = sinput->ops->open(sinput)) < 0) {			sinput->runtime = NULL;			goto __error;		}		sinput->opened = 1;		rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened++;	} else {		sinput = NULL;	}	if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {		if (soutput->opened)			goto __skip_output;		output = kcalloc(1, sizeof(*output), GFP_KERNEL);		if (output == NULL) {			err = -ENOMEM;			goto __error;		}		soutput->runtime = output;		if (snd_rawmidi_init(soutput) < 0) {			if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {				sinput->ops->close(sinput);				sinput->runtime = NULL;			}			err = -ENOMEM;			goto __error;		}		if ((err = soutput->ops->open(soutput)) < 0) {			if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {				sinput->ops->close(sinput);				sinput->runtime = NULL;			}			soutput->runtime = NULL;			goto __error;		}	      __skip_output:		soutput->opened = 1;		if (mode & SNDRV_RAWMIDI_LFLG_APPEND)			soutput->append = 1;	      	if (soutput->use_count++ == 0)			soutput->active_sensing = 1;		rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened++;	} else {		soutput = NULL;	}	if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK))		up(&rmidi->open_mutex);	if (rfile) {		rfile->rmidi = rmidi;		rfile->input = sinput;		rfile->output = soutput;	}	return 0;      __error:	if (input != NULL) {		snd_rawmidi_done_buffer(input);		kfree(input);	}	if (output != NULL) {		snd_rawmidi_done_buffer(output);		kfree(output);	}	module_put(rmidi->card->module);	if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK))		up(&rmidi->open_mutex);      __error1:	return err;}static int snd_rawmidi_open(struct inode *inode, struct file *file){	int maj = imajor(inode);	int cardnum;	snd_card_t *card;	int device, subdevice;	unsigned short fflags;	int err;	snd_rawmidi_t *rmidi;	snd_rawmidi_file_t *rawmidi_file;	wait_queue_t wait;	struct list_head *list;	snd_ctl_file_t *kctl;	switch (maj) {	case CONFIG_SND_MAJOR:		cardnum = SNDRV_MINOR_CARD(iminor(inode));		cardnum %= SNDRV_CARDS;		device = SNDRV_MINOR_DEVICE(iminor(inode)) - SNDRV_MINOR_RAWMIDI;		device %= SNDRV_MINOR_RAWMIDIS;		break;#ifdef CONFIG_SND_OSSEMUL	case SOUND_MAJOR:		cardnum = SNDRV_MINOR_OSS_CARD(iminor(inode));		cardnum %= SNDRV_CARDS;		device = SNDRV_MINOR_OSS_DEVICE(iminor(inode)) == SNDRV_MINOR_OSS_MIDI ?			midi_map[cardnum] : amidi_map[cardnum];		break;#endif	default:		return -ENXIO;	}	rmidi = snd_rawmidi_devices[(cardnum * SNDRV_RAWMIDI_DEVICES) + device];	if (rmidi == NULL)		return -ENODEV;#ifdef CONFIG_SND_OSSEMUL	if (maj == SOUND_MAJOR && !rmidi->ossreg)		return -ENXIO;#endif	if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK)) 		return -EINVAL;		/* invalid combination */	card = rmidi->card;	err = snd_card_file_add(card, file);	if (err < 0)		return -ENODEV;	fflags = snd_rawmidi_file_flags(file);	if ((file->f_flags & O_APPEND) || maj != CONFIG_SND_MAJOR) /* OSS emul? */		fflags |= SNDRV_RAWMIDI_LFLG_APPEND;	fflags |= SNDRV_RAWMIDI_LFLG_NOOPENLOCK;	rawmidi_file = kmalloc(sizeof(*rawmidi_file), GFP_KERNEL);	if (rawmidi_file == NULL) {		snd_card_file_remove(card, file);		return -ENOMEM;	}	init_waitqueue_entry(&wait, current);	add_wait_queue(&rmidi->open_wait, &wait);	down(&rmidi->open_mutex);	while (1) {		subdevice = -1;		down_read(&card->controls_rwsem);		list_for_each(list, &card->ctl_files) {			kctl = snd_ctl_file(list);			if (kctl->pid == current->pid) {				subdevice = kctl->prefer_rawmidi_subdevice;				break;			}		}		up_read(&card->controls_rwsem);		err = snd_rawmidi_kernel_open(cardnum, device, subdevice, fflags, rawmidi_file);

⌨️ 快捷键说明

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