📄 rawmidi.c
字号:
/* * 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(®ister_mutex); rmidi = snd_rawmidi_search(card, device); mutex_unlock(®ister_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 + -