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 + -
显示快捷键?