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

📄 rawmidi.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* *  Abstract layer for MIDI v1.0 stream *  Copyright (c) by Jaroslav Kysela <perex@perex.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/sched.h>#include <linux/slab.h>#include <linux/time.h>#include <linux/wait.h>#include <linux/mutex.h>#include <linux/moduleparam.h>#include <linux/delay.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@perex.cz>");MODULE_DESCRIPTION("Midlevel RawMidi code for ALSA.");MODULE_LICENSE("GPL");#ifdef CONFIG_SND_OSSEMULstatic int midi_map[SNDRV_CARDS];static int amidi_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};module_param_array(midi_map, int, NULL, 0444);MODULE_PARM_DESC(midi_map, "Raw MIDI device number assigned to 1st OSS device.");module_param_array(amidi_map, int, NULL, 0444);MODULE_PARM_DESC(amidi_map, "Raw MIDI device number assigned to 2nd OSS device.");#endif /* CONFIG_SND_OSSEMUL */static int snd_rawmidi_free(struct snd_rawmidi *rawmidi);static int snd_rawmidi_dev_free(struct snd_device *device);static int snd_rawmidi_dev_register(struct snd_device *device);static int snd_rawmidi_dev_disconnect(struct snd_device *device);static LIST_HEAD(snd_rawmidi_devices);static DEFINE_MUTEX(register_mutex);static struct snd_rawmidi *snd_rawmidi_search(struct snd_card *card, int device){	struct snd_rawmidi *rawmidi;	list_for_each_entry(rawmidi, &snd_rawmidi_devices, list)		if (rawmidi->card == card && rawmidi->device == device)			return rawmidi;	return NULL;}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(struct snd_rawmidi_substream *substream){	struct snd_rawmidi_runtime *runtime = substream->runtime;	return runtime->avail >= runtime->avail_min;}static inline int snd_rawmidi_ready_append(struct snd_rawmidi_substream *substream,					   size_t count){	struct snd_rawmidi_runtime *runtime = substream->runtime;	return runtime->avail >= runtime->avail_min &&	       (!substream->append || runtime->avail >= count);}static void snd_rawmidi_input_event_tasklet(unsigned long data){	struct snd_rawmidi_substream *substream = (struct snd_rawmidi_substream *)data;	substream->runtime->event(substream);}static void snd_rawmidi_output_trigger_tasklet(unsigned long data){	struct snd_rawmidi_substream *substream = (struct snd_rawmidi_substream *)data;	substream->ops->trigger(substream, 1);}static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream){	struct snd_rawmidi_runtime *runtime;	if ((runtime = kzalloc(sizeof(*runtime), GFP_KERNEL)) == NULL)		return -ENOMEM;	spin_lock_init(&runtime->lock);	init_waitqueue_head(&runtime->sleep);	if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT)		tasklet_init(&runtime->tasklet,			     snd_rawmidi_input_event_tasklet,			     (unsigned long)substream);	else		tasklet_init(&runtime->tasklet,			     snd_rawmidi_output_trigger_tasklet,			     (unsigned long)substream);	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) {		kfree(runtime);		return -ENOMEM;	}	runtime->appl_ptr = runtime->hw_ptr = 0;	substream->runtime = runtime;	return 0;}static int snd_rawmidi_runtime_free(struct snd_rawmidi_substream *substream){	struct snd_rawmidi_runtime *runtime = substream->runtime;	kfree(runtime->buffer);	kfree(runtime);	substream->runtime = NULL;	return 0;}static inline void snd_rawmidi_output_trigger(struct snd_rawmidi_substream *substream,int up){	if (up) {		tasklet_hi_schedule(&substream->runtime->tasklet);	} else {		tasklet_kill(&substream->runtime->tasklet);		substream->ops->trigger(substream, 0);	}}static void snd_rawmidi_input_trigger(struct snd_rawmidi_substream *substream, int up){	substream->ops->trigger(substream, up);	if (!up && substream->runtime->event)		tasklet_kill(&substream->runtime->tasklet);}int snd_rawmidi_drop_output(struct snd_rawmidi_substream *substream){	unsigned long flags;	struct snd_rawmidi_runtime *runtime = substream->runtime;	snd_rawmidi_output_trigger(substream, 0);	runtime->drain = 0;	spin_lock_irqsave(&runtime->lock, flags);	runtime->appl_ptr = runtime->hw_ptr = 0;	runtime->avail = runtime->buffer_size;	spin_unlock_irqrestore(&runtime->lock, flags);	return 0;}int snd_rawmidi_drain_output(struct snd_rawmidi_substream *substream){	int err;	long timeout;	struct snd_rawmidi_runtime *runtime = substream->runtime;	err = 0;	runtime->drain = 1;	timeout = wait_event_interruptible_timeout(runtime->sleep,				(runtime->avail >= runtime->buffer_size),				10*HZ);	if (signal_pending(current))		err = -ERESTARTSYS;	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;	}	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			msleep(50);		snd_rawmidi_drop_output(substream);	}	return err;}int snd_rawmidi_drain_input(struct snd_rawmidi_substream *substream){	unsigned long flags;	struct snd_rawmidi_runtime *runtime = substream->runtime;	snd_rawmidi_input_trigger(substream, 0);	runtime->drain = 0;	spin_lock_irqsave(&runtime->lock, flags);	runtime->appl_ptr = runtime->hw_ptr = 0;	runtime->avail = 0;	spin_unlock_irqrestore(&runtime->lock, flags);	return 0;}int snd_rawmidi_kernel_open(struct snd_card *card, int device, int subdevice,			    int mode, struct snd_rawmidi_file * rfile){	struct snd_rawmidi *rmidi;	struct list_head *list1, *list2;	struct snd_rawmidi_substream *sinput = NULL, *soutput = NULL;	struct snd_rawmidi_runtime *input = NULL, *output = NULL;	int err;	if (rfile)		rfile->input = rfile->output = NULL;	mutex_lock(&register_mutex);	rmidi = snd_rawmidi_search(card, device);	mutex_unlock(&register_mutex);	if (rmidi == NULL) {		err = -ENODEV;		goto __error1;	}	if (!try_module_get(rmidi->card->module)) {		err = -EFAULT;		goto __error1;	}	if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK))		mutex_lock(&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, struct snd_rawmidi_substream, 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, struct snd_rawmidi_substream, 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) {		if ((err = snd_rawmidi_runtime_create(sinput)) < 0)			goto __error;		input = sinput->runtime;		if ((err = sinput->ops->open(sinput)) < 0)			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;		if ((err = snd_rawmidi_runtime_create(soutput)) < 0) {			if (mode & SNDRV_RAWMIDI_LFLG_INPUT)				sinput->ops->close(sinput);			goto __error;		}		output = soutput->runtime;		if ((err = soutput->ops->open(soutput)) < 0) {			if (mode & SNDRV_RAWMIDI_LFLG_INPUT)				sinput->ops->close(sinput);			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))		mutex_unlock(&rmidi->open_mutex);	if (rfile) {		rfile->rmidi = rmidi;		rfile->input = sinput;		rfile->output = soutput;	}	return 0;      __error:	if (input != NULL)		snd_rawmidi_runtime_free(sinput);	if (output != NULL)		snd_rawmidi_runtime_free(soutput);	module_put(rmidi->card->module);	if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK))		mutex_unlock(&rmidi->open_mutex);      __error1:	return err;}static int snd_rawmidi_open(struct inode *inode, struct file *file){	int maj = imajor(inode);	struct snd_card *card;	int subdevice;	unsigned short fflags;	int err;	struct snd_rawmidi *rmidi;	struct snd_rawmidi_file *rawmidi_file;	wait_queue_t wait;	struct snd_ctl_file *kctl;	if (maj == snd_major) {		rmidi = snd_lookup_minor_data(iminor(inode),					      SNDRV_DEVICE_TYPE_RAWMIDI);#ifdef CONFIG_SND_OSSEMUL	} else if (maj == SOUND_MAJOR) {		rmidi = snd_lookup_oss_minor_data(iminor(inode),						  SNDRV_OSS_DEVICE_TYPE_MIDI);#endif	} else		return -ENXIO;	if (rmidi == NULL)		return -ENODEV;	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 == SOUND_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);	mutex_lock(&rmidi->open_mutex);	while (1) {

⌨️ 快捷键说明

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