add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
#
# Makefile for ALSA
# Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
#
snd-seq-oss-objs := seq_oss.o seq_oss_init.o seq_oss_timer.o seq_oss_ioctl.o \
seq_oss_event.o seq_oss_rw.o seq_oss_synth.o \
seq_oss_midi.o seq_oss_readq.o seq_oss_writeq.o
obj-$(CONFIG_SND_SEQUENCER) += snd-seq-oss.o

View File

@@ -0,0 +1,311 @@
/*
* OSS compatible sequencer driver
*
* registration of device and proc
*
* Copyright (C) 1998,99 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 <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/initval.h>
#include "seq_oss_device.h"
#include "seq_oss_synth.h"
/*
* module option
*/
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("OSS-compatible sequencer module");
MODULE_LICENSE("GPL");
/* Takashi says this is really only for sound-service-0-, but this is OK. */
MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_SEQUENCER);
MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_MUSIC);
#ifdef SNDRV_SEQ_OSS_DEBUG
module_param(seq_oss_debug, int, 0644);
MODULE_PARM_DESC(seq_oss_debug, "debug option");
int seq_oss_debug = 0;
#endif
/*
* prototypes
*/
static int register_device(void);
static void unregister_device(void);
#ifdef CONFIG_PROC_FS
static int register_proc(void);
static void unregister_proc(void);
#else
static inline int register_proc(void) { return 0; }
static inline void unregister_proc(void) {}
#endif
static int odev_open(struct inode *inode, struct file *file);
static int odev_release(struct inode *inode, struct file *file);
static ssize_t odev_read(struct file *file, char __user *buf, size_t count, loff_t *offset);
static ssize_t odev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset);
static long odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
static unsigned int odev_poll(struct file *file, poll_table * wait);
/*
* module interface
*/
static int __init alsa_seq_oss_init(void)
{
int rc;
static struct snd_seq_dev_ops ops = {
snd_seq_oss_synth_register,
snd_seq_oss_synth_unregister,
};
snd_seq_autoload_lock();
if ((rc = register_device()) < 0)
goto error;
if ((rc = register_proc()) < 0) {
unregister_device();
goto error;
}
if ((rc = snd_seq_oss_create_client()) < 0) {
unregister_proc();
unregister_device();
goto error;
}
if ((rc = snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OSS, &ops,
sizeof(struct snd_seq_oss_reg))) < 0) {
snd_seq_oss_delete_client();
unregister_proc();
unregister_device();
goto error;
}
/* success */
snd_seq_oss_synth_init();
error:
snd_seq_autoload_unlock();
return rc;
}
static void __exit alsa_seq_oss_exit(void)
{
snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OSS);
snd_seq_oss_delete_client();
unregister_proc();
unregister_device();
}
module_init(alsa_seq_oss_init)
module_exit(alsa_seq_oss_exit)
/*
* ALSA minor device interface
*/
static DEFINE_MUTEX(register_mutex);
static int
odev_open(struct inode *inode, struct file *file)
{
int level, rc;
if (iminor(inode) == SNDRV_MINOR_OSS_MUSIC)
level = SNDRV_SEQ_OSS_MODE_MUSIC;
else
level = SNDRV_SEQ_OSS_MODE_SYNTH;
mutex_lock(&register_mutex);
rc = snd_seq_oss_open(file, level);
mutex_unlock(&register_mutex);
return rc;
}
static int
odev_release(struct inode *inode, struct file *file)
{
struct seq_oss_devinfo *dp;
if ((dp = file->private_data) == NULL)
return 0;
snd_seq_oss_drain_write(dp);
mutex_lock(&register_mutex);
snd_seq_oss_release(dp);
mutex_unlock(&register_mutex);
return 0;
}
static ssize_t
odev_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
{
struct seq_oss_devinfo *dp;
dp = file->private_data;
if (snd_BUG_ON(!dp))
return -ENXIO;
return snd_seq_oss_read(dp, buf, count);
}
static ssize_t
odev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
{
struct seq_oss_devinfo *dp;
dp = file->private_data;
if (snd_BUG_ON(!dp))
return -ENXIO;
return snd_seq_oss_write(dp, buf, count, file);
}
static long
odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct seq_oss_devinfo *dp;
dp = file->private_data;
if (snd_BUG_ON(!dp))
return -ENXIO;
return snd_seq_oss_ioctl(dp, cmd, arg);
}
#ifdef CONFIG_COMPAT
#define odev_ioctl_compat odev_ioctl
#else
#define odev_ioctl_compat NULL
#endif
static unsigned int
odev_poll(struct file *file, poll_table * wait)
{
struct seq_oss_devinfo *dp;
dp = file->private_data;
if (snd_BUG_ON(!dp))
return -ENXIO;
return snd_seq_oss_poll(dp, file, wait);
}
/*
* registration of sequencer minor device
*/
static const struct file_operations seq_oss_f_ops =
{
.owner = THIS_MODULE,
.read = odev_read,
.write = odev_write,
.open = odev_open,
.release = odev_release,
.poll = odev_poll,
.unlocked_ioctl = odev_ioctl,
.compat_ioctl = odev_ioctl_compat,
};
static int __init
register_device(void)
{
int rc;
mutex_lock(&register_mutex);
if ((rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER,
NULL, 0,
&seq_oss_f_ops, NULL,
SNDRV_SEQ_OSS_DEVNAME)) < 0) {
snd_printk(KERN_ERR "can't register device seq\n");
mutex_unlock(&register_mutex);
return rc;
}
if ((rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC,
NULL, 0,
&seq_oss_f_ops, NULL,
SNDRV_SEQ_OSS_DEVNAME)) < 0) {
snd_printk(KERN_ERR "can't register device music\n");
snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0);
mutex_unlock(&register_mutex);
return rc;
}
debug_printk(("device registered\n"));
mutex_unlock(&register_mutex);
return 0;
}
static void
unregister_device(void)
{
mutex_lock(&register_mutex);
debug_printk(("device unregistered\n"));
if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC, NULL, 0) < 0)
snd_printk(KERN_ERR "error unregister device music\n");
if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0) < 0)
snd_printk(KERN_ERR "error unregister device seq\n");
mutex_unlock(&register_mutex);
}
/*
* /proc interface
*/
#ifdef CONFIG_PROC_FS
static struct snd_info_entry *info_entry;
static void
info_read(struct snd_info_entry *entry, struct snd_info_buffer *buf)
{
mutex_lock(&register_mutex);
snd_iprintf(buf, "OSS sequencer emulation version %s\n", SNDRV_SEQ_OSS_VERSION_STR);
snd_seq_oss_system_info_read(buf);
snd_seq_oss_synth_info_read(buf);
snd_seq_oss_midi_info_read(buf);
mutex_unlock(&register_mutex);
}
static int __init
register_proc(void)
{
struct snd_info_entry *entry;
entry = snd_info_create_module_entry(THIS_MODULE, SNDRV_SEQ_OSS_PROCNAME, snd_seq_root);
if (entry == NULL)
return -ENOMEM;
entry->content = SNDRV_INFO_CONTENT_TEXT;
entry->private_data = NULL;
entry->c.text.read = info_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
return -ENOMEM;
}
info_entry = entry;
return 0;
}
static void
unregister_proc(void)
{
snd_info_free_entry(info_entry);
info_entry = NULL;
}
#endif /* CONFIG_PROC_FS */

View File

@@ -0,0 +1,189 @@
/*
* OSS compatible sequencer driver
*
* Copyright (C) 1998,99 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
*/
#ifndef __SEQ_OSS_DEVICE_H
#define __SEQ_OSS_DEVICE_H
#include <linux/time.h>
#include <linux/wait.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <sound/core.h>
#include <sound/seq_oss.h>
#include <sound/rawmidi.h>
#include <sound/seq_kernel.h>
#include <sound/info.h>
/* enable debug print */
#define SNDRV_SEQ_OSS_DEBUG
/* max. applications */
#define SNDRV_SEQ_OSS_MAX_CLIENTS 16
#define SNDRV_SEQ_OSS_MAX_SYNTH_DEVS 16
#define SNDRV_SEQ_OSS_MAX_MIDI_DEVS 32
/* version */
#define SNDRV_SEQ_OSS_MAJOR_VERSION 0
#define SNDRV_SEQ_OSS_MINOR_VERSION 1
#define SNDRV_SEQ_OSS_TINY_VERSION 8
#define SNDRV_SEQ_OSS_VERSION_STR "0.1.8"
/* device and proc interface name */
#define SNDRV_SEQ_OSS_DEVNAME "seq_oss"
#define SNDRV_SEQ_OSS_PROCNAME "oss"
/*
* type definitions
*/
typedef unsigned int reltime_t;
typedef unsigned int abstime_t;
/*
* synthesizer channel information
*/
struct seq_oss_chinfo {
int note, vel;
};
/*
* synthesizer information
*/
struct seq_oss_synthinfo {
struct snd_seq_oss_arg arg;
struct seq_oss_chinfo *ch;
struct seq_oss_synth_sysex *sysex;
int nr_voices;
int opened;
int is_midi;
int midi_mapped;
};
/*
* sequencer client information
*/
struct seq_oss_devinfo {
int index; /* application index */
int cseq; /* sequencer client number */
int port; /* sequencer port number */
int queue; /* sequencer queue number */
struct snd_seq_addr addr; /* address of this device */
int seq_mode; /* sequencer mode */
int file_mode; /* file access */
/* midi device table */
int max_mididev;
/* synth device table */
int max_synthdev;
struct seq_oss_synthinfo synths[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS];
int synth_opened;
/* output queue */
struct seq_oss_writeq *writeq;
/* midi input queue */
struct seq_oss_readq *readq;
/* timer */
struct seq_oss_timer *timer;
};
/*
* function prototypes
*/
/* create/delete OSS sequencer client */
int snd_seq_oss_create_client(void);
int snd_seq_oss_delete_client(void);
/* device file interface */
int snd_seq_oss_open(struct file *file, int level);
void snd_seq_oss_release(struct seq_oss_devinfo *dp);
int snd_seq_oss_ioctl(struct seq_oss_devinfo *dp, unsigned int cmd, unsigned long arg);
int snd_seq_oss_read(struct seq_oss_devinfo *dev, char __user *buf, int count);
int snd_seq_oss_write(struct seq_oss_devinfo *dp, const char __user *buf, int count, struct file *opt);
unsigned int snd_seq_oss_poll(struct seq_oss_devinfo *dp, struct file *file, poll_table * wait);
void snd_seq_oss_reset(struct seq_oss_devinfo *dp);
void snd_seq_oss_drain_write(struct seq_oss_devinfo *dp);
/* */
void snd_seq_oss_process_queue(struct seq_oss_devinfo *dp, abstime_t time);
/* proc interface */
void snd_seq_oss_system_info_read(struct snd_info_buffer *buf);
void snd_seq_oss_midi_info_read(struct snd_info_buffer *buf);
void snd_seq_oss_synth_info_read(struct snd_info_buffer *buf);
void snd_seq_oss_readq_info_read(struct seq_oss_readq *q, struct snd_info_buffer *buf);
/* file mode macros */
#define is_read_mode(mode) ((mode) & SNDRV_SEQ_OSS_FILE_READ)
#define is_write_mode(mode) ((mode) & SNDRV_SEQ_OSS_FILE_WRITE)
#define is_nonblock_mode(mode) ((mode) & SNDRV_SEQ_OSS_FILE_NONBLOCK)
/* dispatch event */
static inline int
snd_seq_oss_dispatch(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, int atomic, int hop)
{
return snd_seq_kernel_client_dispatch(dp->cseq, ev, atomic, hop);
}
/* ioctl */
static inline int
snd_seq_oss_control(struct seq_oss_devinfo *dp, unsigned int type, void *arg)
{
return snd_seq_kernel_client_ctl(dp->cseq, type, arg);
}
/* fill the addresses in header */
static inline void
snd_seq_oss_fill_addr(struct seq_oss_devinfo *dp, struct snd_seq_event *ev,
int dest_client, int dest_port)
{
ev->queue = dp->queue;
ev->source = dp->addr;
ev->dest.client = dest_client;
ev->dest.port = dest_port;
}
/* misc. functions for proc interface */
char *enabled_str(int bool);
/* for debug */
#ifdef SNDRV_SEQ_OSS_DEBUG
extern int seq_oss_debug;
#define debug_printk(x) do { if (seq_oss_debug > 0) snd_printd x; } while (0)
#else
#define debug_printk(x) /**/
#endif
#endif /* __SEQ_OSS_DEVICE_H */

View File

@@ -0,0 +1,447 @@
/*
* OSS compatible sequencer driver
*
* Copyright (C) 1998,99 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 "seq_oss_device.h"
#include "seq_oss_synth.h"
#include "seq_oss_midi.h"
#include "seq_oss_event.h"
#include "seq_oss_timer.h"
#include <sound/seq_oss_legacy.h>
#include "seq_oss_readq.h"
#include "seq_oss_writeq.h"
/*
* prototypes
*/
static int extended_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev);
static int chn_voice_event(struct seq_oss_devinfo *dp, union evrec *event_rec, struct snd_seq_event *ev);
static int chn_common_event(struct seq_oss_devinfo *dp, union evrec *event_rec, struct snd_seq_event *ev);
static int timing_event(struct seq_oss_devinfo *dp, union evrec *event_rec, struct snd_seq_event *ev);
static int local_event(struct seq_oss_devinfo *dp, union evrec *event_rec, struct snd_seq_event *ev);
static int old_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev);
static int note_on_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, struct snd_seq_event *ev);
static int note_off_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, struct snd_seq_event *ev);
static int set_note_event(struct seq_oss_devinfo *dp, int dev, int type, int ch, int note, int vel, struct snd_seq_event *ev);
static int set_control_event(struct seq_oss_devinfo *dp, int dev, int type, int ch, int param, int val, struct snd_seq_event *ev);
static int set_echo_event(struct seq_oss_devinfo *dp, union evrec *rec, struct snd_seq_event *ev);
/*
* convert an OSS event to ALSA event
* return 0 : enqueued
* non-zero : invalid - ignored
*/
int
snd_seq_oss_process_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev)
{
switch (q->s.code) {
case SEQ_EXTENDED:
return extended_event(dp, q, ev);
case EV_CHN_VOICE:
return chn_voice_event(dp, q, ev);
case EV_CHN_COMMON:
return chn_common_event(dp, q, ev);
case EV_TIMING:
return timing_event(dp, q, ev);
case EV_SEQ_LOCAL:
return local_event(dp, q, ev);
case EV_SYSEX:
return snd_seq_oss_synth_sysex(dp, q->x.dev, q->x.buf, ev);
case SEQ_MIDIPUTC:
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
return -EINVAL;
/* put a midi byte */
if (! is_write_mode(dp->file_mode))
break;
if (snd_seq_oss_midi_open(dp, q->s.dev, SNDRV_SEQ_OSS_FILE_WRITE))
break;
if (snd_seq_oss_midi_filemode(dp, q->s.dev) & SNDRV_SEQ_OSS_FILE_WRITE)
return snd_seq_oss_midi_putc(dp, q->s.dev, q->s.parm1, ev);
break;
case SEQ_ECHO:
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
return -EINVAL;
return set_echo_event(dp, q, ev);
case SEQ_PRIVATE:
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
return -EINVAL;
return snd_seq_oss_synth_raw_event(dp, q->c[1], q->c, ev);
default:
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
return -EINVAL;
return old_event(dp, q, ev);
}
return -EINVAL;
}
/* old type events: mode1 only */
static int
old_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev)
{
switch (q->s.code) {
case SEQ_NOTEOFF:
return note_off_event(dp, 0, q->n.chn, q->n.note, q->n.vel, ev);
case SEQ_NOTEON:
return note_on_event(dp, 0, q->n.chn, q->n.note, q->n.vel, ev);
case SEQ_WAIT:
/* skip */
break;
case SEQ_PGMCHANGE:
return set_control_event(dp, 0, SNDRV_SEQ_EVENT_PGMCHANGE,
q->n.chn, 0, q->n.note, ev);
case SEQ_SYNCTIMER:
return snd_seq_oss_timer_reset(dp->timer);
}
return -EINVAL;
}
/* 8bytes extended event: mode1 only */
static int
extended_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev)
{
int val;
switch (q->e.cmd) {
case SEQ_NOTEOFF:
return note_off_event(dp, q->e.dev, q->e.chn, q->e.p1, q->e.p2, ev);
case SEQ_NOTEON:
return note_on_event(dp, q->e.dev, q->e.chn, q->e.p1, q->e.p2, ev);
case SEQ_PGMCHANGE:
return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_PGMCHANGE,
q->e.chn, 0, q->e.p1, ev);
case SEQ_AFTERTOUCH:
return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_CHANPRESS,
q->e.chn, 0, q->e.p1, ev);
case SEQ_BALANCE:
/* convert -128:127 to 0:127 */
val = (char)q->e.p1;
val = (val + 128) / 2;
return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_CONTROLLER,
q->e.chn, CTL_PAN, val, ev);
case SEQ_CONTROLLER:
val = ((short)q->e.p3 << 8) | (short)q->e.p2;
switch (q->e.p1) {
case CTRL_PITCH_BENDER: /* SEQ1 V2 control */
/* -0x2000:0x1fff */
return set_control_event(dp, q->e.dev,
SNDRV_SEQ_EVENT_PITCHBEND,
q->e.chn, 0, val, ev);
case CTRL_PITCH_BENDER_RANGE:
/* conversion: 100/semitone -> 128/semitone */
return set_control_event(dp, q->e.dev,
SNDRV_SEQ_EVENT_REGPARAM,
q->e.chn, 0, val*128/100, ev);
default:
return set_control_event(dp, q->e.dev,
SNDRV_SEQ_EVENT_CONTROL14,
q->e.chn, q->e.p1, val, ev);
}
case SEQ_VOLMODE:
return snd_seq_oss_synth_raw_event(dp, q->e.dev, q->c, ev);
}
return -EINVAL;
}
/* channel voice events: mode1 and 2 */
static int
chn_voice_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev)
{
if (q->v.chn >= 32)
return -EINVAL;
switch (q->v.cmd) {
case MIDI_NOTEON:
return note_on_event(dp, q->v.dev, q->v.chn, q->v.note, q->v.parm, ev);
case MIDI_NOTEOFF:
return note_off_event(dp, q->v.dev, q->v.chn, q->v.note, q->v.parm, ev);
case MIDI_KEY_PRESSURE:
return set_note_event(dp, q->v.dev, SNDRV_SEQ_EVENT_KEYPRESS,
q->v.chn, q->v.note, q->v.parm, ev);
}
return -EINVAL;
}
/* channel common events: mode1 and 2 */
static int
chn_common_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev)
{
if (q->l.chn >= 32)
return -EINVAL;
switch (q->l.cmd) {
case MIDI_PGM_CHANGE:
return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_PGMCHANGE,
q->l.chn, 0, q->l.p1, ev);
case MIDI_CTL_CHANGE:
return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_CONTROLLER,
q->l.chn, q->l.p1, q->l.val, ev);
case MIDI_PITCH_BEND:
/* conversion: 0:0x3fff -> -0x2000:0x1fff */
return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_PITCHBEND,
q->l.chn, 0, q->l.val - 8192, ev);
case MIDI_CHN_PRESSURE:
return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_CHANPRESS,
q->l.chn, 0, q->l.val, ev);
}
return -EINVAL;
}
/* timer events: mode1 and mode2 */
static int
timing_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev)
{
switch (q->t.cmd) {
case TMR_ECHO:
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
return set_echo_event(dp, q, ev);
else {
union evrec tmp;
memset(&tmp, 0, sizeof(tmp));
/* XXX: only for little-endian! */
tmp.echo = (q->t.time << 8) | SEQ_ECHO;
return set_echo_event(dp, &tmp, ev);
}
case TMR_STOP:
if (dp->seq_mode)
return snd_seq_oss_timer_stop(dp->timer);
return 0;
case TMR_CONTINUE:
if (dp->seq_mode)
return snd_seq_oss_timer_continue(dp->timer);
return 0;
case TMR_TEMPO:
if (dp->seq_mode)
return snd_seq_oss_timer_tempo(dp->timer, q->t.time);
return 0;
}
return -EINVAL;
}
/* local events: mode1 and 2 */
static int
local_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev)
{
return -EINVAL;
}
/*
* process note-on event for OSS synth
* three different modes are available:
* - SNDRV_SEQ_OSS_PROCESS_EVENTS (for one-voice per channel mode)
* Accept note 255 as volume change.
* - SNDRV_SEQ_OSS_PASS_EVENTS
* Pass all events to lowlevel driver anyway
* - SNDRV_SEQ_OSS_PROCESS_KEYPRESS (mostly for Emu8000)
* Use key-pressure if note >= 128
*/
static int
note_on_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, struct snd_seq_event *ev)
{
struct seq_oss_synthinfo *info = &dp->synths[dev];
switch (info->arg.event_passing) {
case SNDRV_SEQ_OSS_PROCESS_EVENTS:
if (! info->ch || ch < 0 || ch >= info->nr_voices) {
/* pass directly */
return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
}
if (note == 255 && info->ch[ch].note >= 0) {
/* volume control */
int type;
//if (! vel)
/* set volume to zero -- note off */
// type = SNDRV_SEQ_EVENT_NOTEOFF;
//else
if (info->ch[ch].vel)
/* sample already started -- volume change */
type = SNDRV_SEQ_EVENT_KEYPRESS;
else
/* sample not started -- start now */
type = SNDRV_SEQ_EVENT_NOTEON;
info->ch[ch].vel = vel;
return set_note_event(dp, dev, type, ch, info->ch[ch].note, vel, ev);
} else if (note >= 128)
return -EINVAL; /* invalid */
if (note != info->ch[ch].note && info->ch[ch].note >= 0)
/* note changed - note off at beginning */
set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, info->ch[ch].note, 0, ev);
/* set current status */
info->ch[ch].note = note;
info->ch[ch].vel = vel;
if (vel) /* non-zero velocity - start the note now */
return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
return -EINVAL;
case SNDRV_SEQ_OSS_PASS_EVENTS:
/* pass the event anyway */
return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
case SNDRV_SEQ_OSS_PROCESS_KEYPRESS:
if (note >= 128) /* key pressure: shifted by 128 */
return set_note_event(dp, dev, SNDRV_SEQ_EVENT_KEYPRESS, ch, note - 128, vel, ev);
else /* normal note-on event */
return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
}
return -EINVAL;
}
/*
* process note-off event for OSS synth
*/
static int
note_off_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, struct snd_seq_event *ev)
{
struct seq_oss_synthinfo *info = &dp->synths[dev];
switch (info->arg.event_passing) {
case SNDRV_SEQ_OSS_PROCESS_EVENTS:
if (! info->ch || ch < 0 || ch >= info->nr_voices) {
/* pass directly */
return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
}
if (info->ch[ch].note >= 0) {
note = info->ch[ch].note;
info->ch[ch].vel = 0;
info->ch[ch].note = -1;
return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, note, vel, ev);
}
return -EINVAL; /* invalid */
case SNDRV_SEQ_OSS_PASS_EVENTS:
case SNDRV_SEQ_OSS_PROCESS_KEYPRESS:
/* pass the event anyway */
return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, note, vel, ev);
}
return -EINVAL;
}
/*
* create a note event
*/
static int
set_note_event(struct seq_oss_devinfo *dp, int dev, int type, int ch, int note, int vel, struct snd_seq_event *ev)
{
if (! snd_seq_oss_synth_is_valid(dp, dev))
return -ENXIO;
ev->type = type;
snd_seq_oss_synth_addr(dp, dev, ev);
ev->data.note.channel = ch;
ev->data.note.note = note;
ev->data.note.velocity = vel;
return 0;
}
/*
* create a control event
*/
static int
set_control_event(struct seq_oss_devinfo *dp, int dev, int type, int ch, int param, int val, struct snd_seq_event *ev)
{
if (! snd_seq_oss_synth_is_valid(dp, dev))
return -ENXIO;
ev->type = type;
snd_seq_oss_synth_addr(dp, dev, ev);
ev->data.control.channel = ch;
ev->data.control.param = param;
ev->data.control.value = val;
return 0;
}
/*
* create an echo event
*/
static int
set_echo_event(struct seq_oss_devinfo *dp, union evrec *rec, struct snd_seq_event *ev)
{
ev->type = SNDRV_SEQ_EVENT_ECHO;
/* echo back to itself */
snd_seq_oss_fill_addr(dp, ev, dp->addr.client, dp->addr.port);
memcpy(&ev->data, rec, LONG_EVENT_SIZE);
return 0;
}
/*
* event input callback from ALSA sequencer:
* the echo event is processed here.
*/
int
snd_seq_oss_event_input(struct snd_seq_event *ev, int direct, void *private_data,
int atomic, int hop)
{
struct seq_oss_devinfo *dp = (struct seq_oss_devinfo *)private_data;
union evrec *rec;
if (ev->type != SNDRV_SEQ_EVENT_ECHO)
return snd_seq_oss_midi_input(ev, direct, private_data);
if (ev->source.client != dp->cseq)
return 0; /* ignored */
rec = (union evrec*)&ev->data;
if (rec->s.code == SEQ_SYNCTIMER) {
/* sync echo back */
snd_seq_oss_writeq_wakeup(dp->writeq, rec->t.time);
} else {
/* echo back event */
if (dp->readq == NULL)
return 0;
snd_seq_oss_readq_put_event(dp->readq, rec);
}
return 0;
}

View File

@@ -0,0 +1,112 @@
/*
* OSS compatible sequencer driver
*
* seq_oss_event.h - OSS event queue record
*
* Copyright (C) 1998,99 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
*/
#ifndef __SEQ_OSS_EVENT_H
#define __SEQ_OSS_EVENT_H
#include "seq_oss_device.h"
#define SHORT_EVENT_SIZE 4
#define LONG_EVENT_SIZE 8
/* short event (4bytes) */
struct evrec_short {
unsigned char code;
unsigned char parm1;
unsigned char dev;
unsigned char parm2;
};
/* short note events (4bytes) */
struct evrec_note {
unsigned char code;
unsigned char chn;
unsigned char note;
unsigned char vel;
};
/* long timer events (8bytes) */
struct evrec_timer {
unsigned char code;
unsigned char cmd;
unsigned char dummy1, dummy2;
unsigned int time;
};
/* long extended events (8bytes) */
struct evrec_extended {
unsigned char code;
unsigned char cmd;
unsigned char dev;
unsigned char chn;
unsigned char p1, p2, p3, p4;
};
/* long channel events (8bytes) */
struct evrec_long {
unsigned char code;
unsigned char dev;
unsigned char cmd;
unsigned char chn;
unsigned char p1, p2;
unsigned short val;
};
/* channel voice events (8bytes) */
struct evrec_voice {
unsigned char code;
unsigned char dev;
unsigned char cmd;
unsigned char chn;
unsigned char note, parm;
unsigned short dummy;
};
/* sysex events (8bytes) */
struct evrec_sysex {
unsigned char code;
unsigned char dev;
unsigned char buf[6];
};
/* event record */
union evrec {
struct evrec_short s;
struct evrec_note n;
struct evrec_long l;
struct evrec_voice v;
struct evrec_timer t;
struct evrec_extended e;
struct evrec_sysex x;
unsigned int echo;
unsigned char c[LONG_EVENT_SIZE];
};
#define ev_is_long(ev) ((ev)->s.code >= 128)
#define ev_length(ev) ((ev)->s.code >= 128 ? LONG_EVENT_SIZE : SHORT_EVENT_SIZE)
int snd_seq_oss_process_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev);
int snd_seq_oss_process_timer_event(struct seq_oss_timer *rec, union evrec *q);
int snd_seq_oss_event_input(struct snd_seq_event *ev, int direct, void *private_data, int atomic, int hop);
#endif /* __SEQ_OSS_EVENT_H */

View File

@@ -0,0 +1,544 @@
/*
* OSS compatible sequencer driver
*
* open/close and reset interface
*
* Copyright (C) 1998-1999 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 "seq_oss_device.h"
#include "seq_oss_synth.h"
#include "seq_oss_midi.h"
#include "seq_oss_writeq.h"
#include "seq_oss_readq.h"
#include "seq_oss_timer.h"
#include "seq_oss_event.h"
#include <linux/init.h>
#include <linux/moduleparam.h>
/*
* common variables
*/
static int maxqlen = SNDRV_SEQ_OSS_MAX_QLEN;
module_param(maxqlen, int, 0444);
MODULE_PARM_DESC(maxqlen, "maximum queue length");
static int system_client = -1; /* ALSA sequencer client number */
static int system_port = -1;
static int num_clients;
static struct seq_oss_devinfo *client_table[SNDRV_SEQ_OSS_MAX_CLIENTS];
/*
* prototypes
*/
static int receive_announce(struct snd_seq_event *ev, int direct, void *private, int atomic, int hop);
static int translate_mode(struct file *file);
static int create_port(struct seq_oss_devinfo *dp);
static int delete_port(struct seq_oss_devinfo *dp);
static int alloc_seq_queue(struct seq_oss_devinfo *dp);
static int delete_seq_queue(int queue);
static void free_devinfo(void *private);
#define call_ctl(type,rec) snd_seq_kernel_client_ctl(system_client, type, rec)
/*
* create sequencer client for OSS sequencer
*/
int __init
snd_seq_oss_create_client(void)
{
int rc;
struct snd_seq_port_info *port;
struct snd_seq_port_callback port_callback;
port = kmalloc(sizeof(*port), GFP_KERNEL);
if (!port) {
rc = -ENOMEM;
goto __error;
}
/* create ALSA client */
rc = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_OSS,
"OSS sequencer");
if (rc < 0)
goto __error;
system_client = rc;
debug_printk(("new client = %d\n", rc));
/* look up midi devices */
snd_seq_oss_midi_lookup_ports(system_client);
/* create annoucement receiver port */
memset(port, 0, sizeof(*port));
strcpy(port->name, "Receiver");
port->addr.client = system_client;
port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* receive only */
port->type = 0;
memset(&port_callback, 0, sizeof(port_callback));
/* don't set port_callback.owner here. otherwise the module counter
* is incremented and we can no longer release the module..
*/
port_callback.event_input = receive_announce;
port->kernel = &port_callback;
call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, port);
if ((system_port = port->addr.port) >= 0) {
struct snd_seq_port_subscribe subs;
memset(&subs, 0, sizeof(subs));
subs.sender.client = SNDRV_SEQ_CLIENT_SYSTEM;
subs.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
subs.dest.client = system_client;
subs.dest.port = system_port;
call_ctl(SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs);
}
rc = 0;
__error:
kfree(port);
return rc;
}
/*
* receive annoucement from system port, and check the midi device
*/
static int
receive_announce(struct snd_seq_event *ev, int direct, void *private, int atomic, int hop)
{
struct snd_seq_port_info pinfo;
if (atomic)
return 0; /* it must not happen */
switch (ev->type) {
case SNDRV_SEQ_EVENT_PORT_START:
case SNDRV_SEQ_EVENT_PORT_CHANGE:
if (ev->data.addr.client == system_client)
break; /* ignore myself */
memset(&pinfo, 0, sizeof(pinfo));
pinfo.addr = ev->data.addr;
if (call_ctl(SNDRV_SEQ_IOCTL_GET_PORT_INFO, &pinfo) >= 0)
snd_seq_oss_midi_check_new_port(&pinfo);
break;
case SNDRV_SEQ_EVENT_PORT_EXIT:
if (ev->data.addr.client == system_client)
break; /* ignore myself */
snd_seq_oss_midi_check_exit_port(ev->data.addr.client,
ev->data.addr.port);
break;
}
return 0;
}
/*
* delete OSS sequencer client
*/
int
snd_seq_oss_delete_client(void)
{
if (system_client >= 0)
snd_seq_delete_kernel_client(system_client);
snd_seq_oss_midi_clear_all();
return 0;
}
/*
* open sequencer device
*/
int
snd_seq_oss_open(struct file *file, int level)
{
int i, rc;
struct seq_oss_devinfo *dp;
dp = kzalloc(sizeof(*dp), GFP_KERNEL);
if (!dp) {
snd_printk(KERN_ERR "can't malloc device info\n");
return -ENOMEM;
}
debug_printk(("oss_open: dp = %p\n", dp));
dp->cseq = system_client;
dp->port = -1;
dp->queue = -1;
for (i = 0; i < SNDRV_SEQ_OSS_MAX_CLIENTS; i++) {
if (client_table[i] == NULL)
break;
}
dp->index = i;
if (i >= SNDRV_SEQ_OSS_MAX_CLIENTS) {
snd_printk(KERN_ERR "too many applications\n");
rc = -ENOMEM;
goto _error;
}
/* look up synth and midi devices */
snd_seq_oss_synth_setup(dp);
snd_seq_oss_midi_setup(dp);
if (dp->synth_opened == 0 && dp->max_mididev == 0) {
/* snd_printk(KERN_ERR "no device found\n"); */
rc = -ENODEV;
goto _error;
}
/* create port */
debug_printk(("create new port\n"));
rc = create_port(dp);
if (rc < 0) {
snd_printk(KERN_ERR "can't create port\n");
goto _error;
}
/* allocate queue */
debug_printk(("allocate queue\n"));
rc = alloc_seq_queue(dp);
if (rc < 0)
goto _error;
/* set address */
dp->addr.client = dp->cseq;
dp->addr.port = dp->port;
/*dp->addr.queue = dp->queue;*/
/*dp->addr.channel = 0;*/
dp->seq_mode = level;
/* set up file mode */
dp->file_mode = translate_mode(file);
/* initialize read queue */
debug_printk(("initialize read queue\n"));
if (is_read_mode(dp->file_mode)) {
dp->readq = snd_seq_oss_readq_new(dp, maxqlen);
if (!dp->readq) {
rc = -ENOMEM;
goto _error;
}
}
/* initialize write queue */
debug_printk(("initialize write queue\n"));
if (is_write_mode(dp->file_mode)) {
dp->writeq = snd_seq_oss_writeq_new(dp, maxqlen);
if (!dp->writeq) {
rc = -ENOMEM;
goto _error;
}
}
/* initialize timer */
debug_printk(("initialize timer\n"));
dp->timer = snd_seq_oss_timer_new(dp);
if (!dp->timer) {
snd_printk(KERN_ERR "can't alloc timer\n");
rc = -ENOMEM;
goto _error;
}
debug_printk(("timer initialized\n"));
/* set private data pointer */
file->private_data = dp;
/* set up for mode2 */
if (level == SNDRV_SEQ_OSS_MODE_MUSIC)
snd_seq_oss_synth_setup_midi(dp);
else if (is_read_mode(dp->file_mode))
snd_seq_oss_midi_open_all(dp, SNDRV_SEQ_OSS_FILE_READ);
client_table[dp->index] = dp;
num_clients++;
debug_printk(("open done\n"));
return 0;
_error:
snd_seq_oss_synth_cleanup(dp);
snd_seq_oss_midi_cleanup(dp);
delete_seq_queue(dp->queue);
delete_port(dp);
return rc;
}
/*
* translate file flags to private mode
*/
static int
translate_mode(struct file *file)
{
int file_mode = 0;
if ((file->f_flags & O_ACCMODE) != O_RDONLY)
file_mode |= SNDRV_SEQ_OSS_FILE_WRITE;
if ((file->f_flags & O_ACCMODE) != O_WRONLY)
file_mode |= SNDRV_SEQ_OSS_FILE_READ;
if (file->f_flags & O_NONBLOCK)
file_mode |= SNDRV_SEQ_OSS_FILE_NONBLOCK;
return file_mode;
}
/*
* create sequencer port
*/
static int
create_port(struct seq_oss_devinfo *dp)
{
int rc;
struct snd_seq_port_info port;
struct snd_seq_port_callback callback;
memset(&port, 0, sizeof(port));
port.addr.client = dp->cseq;
sprintf(port.name, "Sequencer-%d", dp->index);
port.capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_WRITE; /* no subscription */
port.type = SNDRV_SEQ_PORT_TYPE_SPECIFIC;
port.midi_channels = 128;
port.synth_voices = 128;
memset(&callback, 0, sizeof(callback));
callback.owner = THIS_MODULE;
callback.private_data = dp;
callback.event_input = snd_seq_oss_event_input;
callback.private_free = free_devinfo;
port.kernel = &callback;
rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, &port);
if (rc < 0)
return rc;
dp->port = port.addr.port;
debug_printk(("new port = %d\n", port.addr.port));
return 0;
}
/*
* delete ALSA port
*/
static int
delete_port(struct seq_oss_devinfo *dp)
{
if (dp->port < 0) {
kfree(dp);
return 0;
}
debug_printk(("delete_port %i\n", dp->port));
return snd_seq_event_port_detach(dp->cseq, dp->port);
}
/*
* allocate a queue
*/
static int
alloc_seq_queue(struct seq_oss_devinfo *dp)
{
struct snd_seq_queue_info qinfo;
int rc;
memset(&qinfo, 0, sizeof(qinfo));
qinfo.owner = system_client;
qinfo.locked = 1;
strcpy(qinfo.name, "OSS Sequencer Emulation");
if ((rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_QUEUE, &qinfo)) < 0)
return rc;
dp->queue = qinfo.queue;
return 0;
}
/*
* release queue
*/
static int
delete_seq_queue(int queue)
{
struct snd_seq_queue_info qinfo;
int rc;
if (queue < 0)
return 0;
memset(&qinfo, 0, sizeof(qinfo));
qinfo.queue = queue;
rc = call_ctl(SNDRV_SEQ_IOCTL_DELETE_QUEUE, &qinfo);
if (rc < 0)
printk(KERN_ERR "seq-oss: unable to delete queue %d (%d)\n", queue, rc);
return rc;
}
/*
* free device informations - private_free callback of port
*/
static void
free_devinfo(void *private)
{
struct seq_oss_devinfo *dp = (struct seq_oss_devinfo *)private;
if (dp->timer)
snd_seq_oss_timer_delete(dp->timer);
if (dp->writeq)
snd_seq_oss_writeq_delete(dp->writeq);
if (dp->readq)
snd_seq_oss_readq_delete(dp->readq);
kfree(dp);
}
/*
* close sequencer device
*/
void
snd_seq_oss_release(struct seq_oss_devinfo *dp)
{
int queue;
client_table[dp->index] = NULL;
num_clients--;
debug_printk(("resetting..\n"));
snd_seq_oss_reset(dp);
debug_printk(("cleaning up..\n"));
snd_seq_oss_synth_cleanup(dp);
snd_seq_oss_midi_cleanup(dp);
/* clear slot */
debug_printk(("releasing resource..\n"));
queue = dp->queue;
if (dp->port >= 0)
delete_port(dp);
delete_seq_queue(queue);
debug_printk(("release done\n"));
}
/*
* Wait until the queue is empty (if we don't have nonblock)
*/
void
snd_seq_oss_drain_write(struct seq_oss_devinfo *dp)
{
if (! dp->timer->running)
return;
if (is_write_mode(dp->file_mode) && !is_nonblock_mode(dp->file_mode) &&
dp->writeq) {
debug_printk(("syncing..\n"));
while (snd_seq_oss_writeq_sync(dp->writeq))
;
}
}
/*
* reset sequencer devices
*/
void
snd_seq_oss_reset(struct seq_oss_devinfo *dp)
{
int i;
/* reset all synth devices */
for (i = 0; i < dp->max_synthdev; i++)
snd_seq_oss_synth_reset(dp, i);
/* reset all midi devices */
if (dp->seq_mode != SNDRV_SEQ_OSS_MODE_MUSIC) {
for (i = 0; i < dp->max_mididev; i++)
snd_seq_oss_midi_reset(dp, i);
}
/* remove queues */
if (dp->readq)
snd_seq_oss_readq_clear(dp->readq);
if (dp->writeq)
snd_seq_oss_writeq_clear(dp->writeq);
/* reset timer */
snd_seq_oss_timer_stop(dp->timer);
}
#ifdef CONFIG_PROC_FS
/*
* misc. functions for proc interface
*/
char *
enabled_str(int bool)
{
return bool ? "enabled" : "disabled";
}
static char *
filemode_str(int val)
{
static char *str[] = {
"none", "read", "write", "read/write",
};
return str[val & SNDRV_SEQ_OSS_FILE_ACMODE];
}
/*
* proc interface
*/
void
snd_seq_oss_system_info_read(struct snd_info_buffer *buf)
{
int i;
struct seq_oss_devinfo *dp;
snd_iprintf(buf, "ALSA client number %d\n", system_client);
snd_iprintf(buf, "ALSA receiver port %d\n", system_port);
snd_iprintf(buf, "\nNumber of applications: %d\n", num_clients);
for (i = 0; i < num_clients; i++) {
snd_iprintf(buf, "\nApplication %d: ", i);
if ((dp = client_table[i]) == NULL) {
snd_iprintf(buf, "*empty*\n");
continue;
}
snd_iprintf(buf, "port %d : queue %d\n", dp->port, dp->queue);
snd_iprintf(buf, " sequencer mode = %s : file open mode = %s\n",
(dp->seq_mode ? "music" : "synth"),
filemode_str(dp->file_mode));
if (dp->seq_mode)
snd_iprintf(buf, " timer tempo = %d, timebase = %d\n",
dp->timer->oss_tempo, dp->timer->oss_timebase);
snd_iprintf(buf, " max queue length %d\n", maxqlen);
if (is_read_mode(dp->file_mode) && dp->readq)
snd_seq_oss_readq_info_read(dp->readq, buf);
}
}
#endif /* CONFIG_PROC_FS */

View File

@@ -0,0 +1,209 @@
/*
* OSS compatible sequencer driver
*
* OSS compatible i/o control
*
* Copyright (C) 1998,99 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 "seq_oss_device.h"
#include "seq_oss_readq.h"
#include "seq_oss_writeq.h"
#include "seq_oss_timer.h"
#include "seq_oss_synth.h"
#include "seq_oss_midi.h"
#include "seq_oss_event.h"
static int snd_seq_oss_synth_info_user(struct seq_oss_devinfo *dp, void __user *arg)
{
struct synth_info info;
if (copy_from_user(&info, arg, sizeof(info)))
return -EFAULT;
if (snd_seq_oss_synth_make_info(dp, info.device, &info) < 0)
return -EINVAL;
if (copy_to_user(arg, &info, sizeof(info)))
return -EFAULT;
return 0;
}
static int snd_seq_oss_midi_info_user(struct seq_oss_devinfo *dp, void __user *arg)
{
struct midi_info info;
if (copy_from_user(&info, arg, sizeof(info)))
return -EFAULT;
if (snd_seq_oss_midi_make_info(dp, info.device, &info) < 0)
return -EINVAL;
if (copy_to_user(arg, &info, sizeof(info)))
return -EFAULT;
return 0;
}
static int snd_seq_oss_oob_user(struct seq_oss_devinfo *dp, void __user *arg)
{
unsigned char ev[8];
struct snd_seq_event tmpev;
if (copy_from_user(ev, arg, 8))
return -EFAULT;
memset(&tmpev, 0, sizeof(tmpev));
snd_seq_oss_fill_addr(dp, &tmpev, dp->addr.port, dp->addr.client);
tmpev.time.tick = 0;
if (! snd_seq_oss_process_event(dp, (union evrec *)ev, &tmpev)) {
snd_seq_oss_dispatch(dp, &tmpev, 0, 0);
}
return 0;
}
int
snd_seq_oss_ioctl(struct seq_oss_devinfo *dp, unsigned int cmd, unsigned long carg)
{
int dev, val;
void __user *arg = (void __user *)carg;
int __user *p = arg;
switch (cmd) {
case SNDCTL_TMR_TIMEBASE:
case SNDCTL_TMR_TEMPO:
case SNDCTL_TMR_START:
case SNDCTL_TMR_STOP:
case SNDCTL_TMR_CONTINUE:
case SNDCTL_TMR_METRONOME:
case SNDCTL_TMR_SOURCE:
case SNDCTL_TMR_SELECT:
case SNDCTL_SEQ_CTRLRATE:
return snd_seq_oss_timer_ioctl(dp->timer, cmd, arg);
case SNDCTL_SEQ_PANIC:
debug_printk(("panic\n"));
snd_seq_oss_reset(dp);
return -EINVAL;
case SNDCTL_SEQ_SYNC:
debug_printk(("sync\n"));
if (! is_write_mode(dp->file_mode) || dp->writeq == NULL)
return 0;
while (snd_seq_oss_writeq_sync(dp->writeq))
;
if (signal_pending(current))
return -ERESTARTSYS;
return 0;
case SNDCTL_SEQ_RESET:
debug_printk(("reset\n"));
snd_seq_oss_reset(dp);
return 0;
case SNDCTL_SEQ_TESTMIDI:
debug_printk(("test midi\n"));
if (get_user(dev, p))
return -EFAULT;
return snd_seq_oss_midi_open(dp, dev, dp->file_mode);
case SNDCTL_SEQ_GETINCOUNT:
debug_printk(("get in count\n"));
if (dp->readq == NULL || ! is_read_mode(dp->file_mode))
return 0;
return put_user(dp->readq->qlen, p) ? -EFAULT : 0;
case SNDCTL_SEQ_GETOUTCOUNT:
debug_printk(("get out count\n"));
if (! is_write_mode(dp->file_mode) || dp->writeq == NULL)
return 0;
return put_user(snd_seq_oss_writeq_get_free_size(dp->writeq), p) ? -EFAULT : 0;
case SNDCTL_SEQ_GETTIME:
debug_printk(("get time\n"));
return put_user(snd_seq_oss_timer_cur_tick(dp->timer), p) ? -EFAULT : 0;
case SNDCTL_SEQ_RESETSAMPLES:
debug_printk(("reset samples\n"));
if (get_user(dev, p))
return -EFAULT;
return snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
case SNDCTL_SEQ_NRSYNTHS:
debug_printk(("nr synths\n"));
return put_user(dp->max_synthdev, p) ? -EFAULT : 0;
case SNDCTL_SEQ_NRMIDIS:
debug_printk(("nr midis\n"));
return put_user(dp->max_mididev, p) ? -EFAULT : 0;
case SNDCTL_SYNTH_MEMAVL:
debug_printk(("mem avail\n"));
if (get_user(dev, p))
return -EFAULT;
val = snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
return put_user(val, p) ? -EFAULT : 0;
case SNDCTL_FM_4OP_ENABLE:
debug_printk(("4op\n"));
if (get_user(dev, p))
return -EFAULT;
snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
return 0;
case SNDCTL_SYNTH_INFO:
case SNDCTL_SYNTH_ID:
debug_printk(("synth info\n"));
return snd_seq_oss_synth_info_user(dp, arg);
case SNDCTL_SEQ_OUTOFBAND:
debug_printk(("out of band\n"));
return snd_seq_oss_oob_user(dp, arg);
case SNDCTL_MIDI_INFO:
debug_printk(("midi info\n"));
return snd_seq_oss_midi_info_user(dp, arg);
case SNDCTL_SEQ_THRESHOLD:
debug_printk(("threshold\n"));
if (! is_write_mode(dp->file_mode))
return 0;
if (get_user(val, p))
return -EFAULT;
if (val < 1)
val = 1;
if (val >= dp->writeq->maxlen)
val = dp->writeq->maxlen - 1;
snd_seq_oss_writeq_set_output(dp->writeq, val);
return 0;
case SNDCTL_MIDI_PRETIME:
debug_printk(("pretime\n"));
if (dp->readq == NULL || !is_read_mode(dp->file_mode))
return 0;
if (get_user(val, p))
return -EFAULT;
if (val <= 0)
val = -1;
else
val = (HZ * val) / 10;
dp->readq->pre_event_timeout = val;
return put_user(val, p) ? -EFAULT : 0;
default:
debug_printk(("others\n"));
if (! is_write_mode(dp->file_mode))
return -EIO;
return snd_seq_oss_synth_ioctl(dp, 0, cmd, carg);
}
return 0;
}

View File

@@ -0,0 +1,713 @@
/*
* OSS compatible sequencer driver
*
* MIDI device handlers
*
* Copyright (C) 1998,99 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/asoundef.h>
#include "seq_oss_midi.h"
#include "seq_oss_readq.h"
#include "seq_oss_timer.h"
#include "seq_oss_event.h"
#include <sound/seq_midi_event.h>
#include "../seq_lock.h"
#include <linux/init.h>
/*
* constants
*/
#define SNDRV_SEQ_OSS_MAX_MIDI_NAME 30
/*
* definition of midi device record
*/
struct seq_oss_midi {
int seq_device; /* device number */
int client; /* sequencer client number */
int port; /* sequencer port number */
unsigned int flags; /* port capability */
int opened; /* flag for opening */
unsigned char name[SNDRV_SEQ_OSS_MAX_MIDI_NAME];
struct snd_midi_event *coder; /* MIDI event coder */
struct seq_oss_devinfo *devinfo; /* assigned OSSseq device */
snd_use_lock_t use_lock;
};
/*
* midi device table
*/
static int max_midi_devs;
static struct seq_oss_midi *midi_devs[SNDRV_SEQ_OSS_MAX_MIDI_DEVS];
static DEFINE_SPINLOCK(register_lock);
/*
* prototypes
*/
static struct seq_oss_midi *get_mdev(int dev);
static struct seq_oss_midi *get_mididev(struct seq_oss_devinfo *dp, int dev);
static int send_synth_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, int dev);
static int send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, struct seq_oss_midi *mdev);
/*
* look up the existing ports
* this looks a very exhausting job.
*/
int __init
snd_seq_oss_midi_lookup_ports(int client)
{
struct snd_seq_client_info *clinfo;
struct snd_seq_port_info *pinfo;
clinfo = kzalloc(sizeof(*clinfo), GFP_KERNEL);
pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
if (! clinfo || ! pinfo) {
kfree(clinfo);
kfree(pinfo);
return -ENOMEM;
}
clinfo->client = -1;
while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, clinfo) == 0) {
if (clinfo->client == client)
continue; /* ignore myself */
pinfo->addr.client = clinfo->client;
pinfo->addr.port = -1;
while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, pinfo) == 0)
snd_seq_oss_midi_check_new_port(pinfo);
}
kfree(clinfo);
kfree(pinfo);
return 0;
}
/*
*/
static struct seq_oss_midi *
get_mdev(int dev)
{
struct seq_oss_midi *mdev;
unsigned long flags;
spin_lock_irqsave(&register_lock, flags);
mdev = midi_devs[dev];
if (mdev)
snd_use_lock_use(&mdev->use_lock);
spin_unlock_irqrestore(&register_lock, flags);
return mdev;
}
/*
* look for the identical slot
*/
static struct seq_oss_midi *
find_slot(int client, int port)
{
int i;
struct seq_oss_midi *mdev;
unsigned long flags;
spin_lock_irqsave(&register_lock, flags);
for (i = 0; i < max_midi_devs; i++) {
mdev = midi_devs[i];
if (mdev && mdev->client == client && mdev->port == port) {
/* found! */
snd_use_lock_use(&mdev->use_lock);
spin_unlock_irqrestore(&register_lock, flags);
return mdev;
}
}
spin_unlock_irqrestore(&register_lock, flags);
return NULL;
}
#define PERM_WRITE (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_SUBS_WRITE)
#define PERM_READ (SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ)
/*
* register a new port if it doesn't exist yet
*/
int
snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo)
{
int i;
struct seq_oss_midi *mdev;
unsigned long flags;
debug_printk(("check for MIDI client %d port %d\n", pinfo->addr.client, pinfo->addr.port));
/* the port must include generic midi */
if (! (pinfo->type & SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC))
return 0;
/* either read or write subscribable */
if ((pinfo->capability & PERM_WRITE) != PERM_WRITE &&
(pinfo->capability & PERM_READ) != PERM_READ)
return 0;
/*
* look for the identical slot
*/
if ((mdev = find_slot(pinfo->addr.client, pinfo->addr.port)) != NULL) {
/* already exists */
snd_use_lock_free(&mdev->use_lock);
return 0;
}
/*
* allocate midi info record
*/
if ((mdev = kzalloc(sizeof(*mdev), GFP_KERNEL)) == NULL) {
snd_printk(KERN_ERR "can't malloc midi info\n");
return -ENOMEM;
}
/* copy the port information */
mdev->client = pinfo->addr.client;
mdev->port = pinfo->addr.port;
mdev->flags = pinfo->capability;
mdev->opened = 0;
snd_use_lock_init(&mdev->use_lock);
/* copy and truncate the name of synth device */
strlcpy(mdev->name, pinfo->name, sizeof(mdev->name));
/* create MIDI coder */
if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &mdev->coder) < 0) {
snd_printk(KERN_ERR "can't malloc midi coder\n");
kfree(mdev);
return -ENOMEM;
}
/* OSS sequencer adds running status to all sequences */
snd_midi_event_no_status(mdev->coder, 1);
/*
* look for en empty slot
*/
spin_lock_irqsave(&register_lock, flags);
for (i = 0; i < max_midi_devs; i++) {
if (midi_devs[i] == NULL)
break;
}
if (i >= max_midi_devs) {
if (max_midi_devs >= SNDRV_SEQ_OSS_MAX_MIDI_DEVS) {
spin_unlock_irqrestore(&register_lock, flags);
snd_midi_event_free(mdev->coder);
kfree(mdev);
return -ENOMEM;
}
max_midi_devs++;
}
mdev->seq_device = i;
midi_devs[mdev->seq_device] = mdev;
spin_unlock_irqrestore(&register_lock, flags);
return 0;
}
/*
* release the midi device if it was registered
*/
int
snd_seq_oss_midi_check_exit_port(int client, int port)
{
struct seq_oss_midi *mdev;
unsigned long flags;
int index;
if ((mdev = find_slot(client, port)) != NULL) {
spin_lock_irqsave(&register_lock, flags);
midi_devs[mdev->seq_device] = NULL;
spin_unlock_irqrestore(&register_lock, flags);
snd_use_lock_free(&mdev->use_lock);
snd_use_lock_sync(&mdev->use_lock);
if (mdev->coder)
snd_midi_event_free(mdev->coder);
kfree(mdev);
}
spin_lock_irqsave(&register_lock, flags);
for (index = max_midi_devs - 1; index >= 0; index--) {
if (midi_devs[index])
break;
}
max_midi_devs = index + 1;
spin_unlock_irqrestore(&register_lock, flags);
return 0;
}
/*
* release the midi device if it was registered
*/
void
snd_seq_oss_midi_clear_all(void)
{
int i;
struct seq_oss_midi *mdev;
unsigned long flags;
spin_lock_irqsave(&register_lock, flags);
for (i = 0; i < max_midi_devs; i++) {
if ((mdev = midi_devs[i]) != NULL) {
if (mdev->coder)
snd_midi_event_free(mdev->coder);
kfree(mdev);
midi_devs[i] = NULL;
}
}
max_midi_devs = 0;
spin_unlock_irqrestore(&register_lock, flags);
}
/*
* set up midi tables
*/
void
snd_seq_oss_midi_setup(struct seq_oss_devinfo *dp)
{
dp->max_mididev = max_midi_devs;
}
/*
* clean up midi tables
*/
void
snd_seq_oss_midi_cleanup(struct seq_oss_devinfo *dp)
{
int i;
for (i = 0; i < dp->max_mididev; i++)
snd_seq_oss_midi_close(dp, i);
dp->max_mididev = 0;
}
/*
* open all midi devices. ignore errors.
*/
void
snd_seq_oss_midi_open_all(struct seq_oss_devinfo *dp, int file_mode)
{
int i;
for (i = 0; i < dp->max_mididev; i++)
snd_seq_oss_midi_open(dp, i, file_mode);
}
/*
* get the midi device information
*/
static struct seq_oss_midi *
get_mididev(struct seq_oss_devinfo *dp, int dev)
{
if (dev < 0 || dev >= dp->max_mididev)
return NULL;
return get_mdev(dev);
}
/*
* open the midi device if not opened yet
*/
int
snd_seq_oss_midi_open(struct seq_oss_devinfo *dp, int dev, int fmode)
{
int perm;
struct seq_oss_midi *mdev;
struct snd_seq_port_subscribe subs;
if ((mdev = get_mididev(dp, dev)) == NULL)
return -ENODEV;
/* already used? */
if (mdev->opened && mdev->devinfo != dp) {
snd_use_lock_free(&mdev->use_lock);
return -EBUSY;
}
perm = 0;
if (is_write_mode(fmode))
perm |= PERM_WRITE;
if (is_read_mode(fmode))
perm |= PERM_READ;
perm &= mdev->flags;
if (perm == 0) {
snd_use_lock_free(&mdev->use_lock);
return -ENXIO;
}
/* already opened? */
if ((mdev->opened & perm) == perm) {
snd_use_lock_free(&mdev->use_lock);
return 0;
}
perm &= ~mdev->opened;
memset(&subs, 0, sizeof(subs));
if (perm & PERM_WRITE) {
subs.sender = dp->addr;
subs.dest.client = mdev->client;
subs.dest.port = mdev->port;
if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0)
mdev->opened |= PERM_WRITE;
}
if (perm & PERM_READ) {
subs.sender.client = mdev->client;
subs.sender.port = mdev->port;
subs.dest = dp->addr;
subs.flags = SNDRV_SEQ_PORT_SUBS_TIMESTAMP;
subs.queue = dp->queue; /* queue for timestamps */
if (snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs) >= 0)
mdev->opened |= PERM_READ;
}
if (! mdev->opened) {
snd_use_lock_free(&mdev->use_lock);
return -ENXIO;
}
mdev->devinfo = dp;
snd_use_lock_free(&mdev->use_lock);
return 0;
}
/*
* close the midi device if already opened
*/
int
snd_seq_oss_midi_close(struct seq_oss_devinfo *dp, int dev)
{
struct seq_oss_midi *mdev;
struct snd_seq_port_subscribe subs;
if ((mdev = get_mididev(dp, dev)) == NULL)
return -ENODEV;
if (! mdev->opened || mdev->devinfo != dp) {
snd_use_lock_free(&mdev->use_lock);
return 0;
}
debug_printk(("closing client %d port %d mode %d\n", mdev->client, mdev->port, mdev->opened));
memset(&subs, 0, sizeof(subs));
if (mdev->opened & PERM_WRITE) {
subs.sender = dp->addr;
subs.dest.client = mdev->client;
subs.dest.port = mdev->port;
snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, &subs);
}
if (mdev->opened & PERM_READ) {
subs.sender.client = mdev->client;
subs.sender.port = mdev->port;
subs.dest = dp->addr;
snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, &subs);
}
mdev->opened = 0;
mdev->devinfo = NULL;
snd_use_lock_free(&mdev->use_lock);
return 0;
}
/*
* change seq capability flags to file mode flags
*/
int
snd_seq_oss_midi_filemode(struct seq_oss_devinfo *dp, int dev)
{
struct seq_oss_midi *mdev;
int mode;
if ((mdev = get_mididev(dp, dev)) == NULL)
return 0;
mode = 0;
if (mdev->opened & PERM_WRITE)
mode |= SNDRV_SEQ_OSS_FILE_WRITE;
if (mdev->opened & PERM_READ)
mode |= SNDRV_SEQ_OSS_FILE_READ;
snd_use_lock_free(&mdev->use_lock);
return mode;
}
/*
* reset the midi device and close it:
* so far, only close the device.
*/
void
snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev)
{
struct seq_oss_midi *mdev;
if ((mdev = get_mididev(dp, dev)) == NULL)
return;
if (! mdev->opened) {
snd_use_lock_free(&mdev->use_lock);
return;
}
if (mdev->opened & PERM_WRITE) {
struct snd_seq_event ev;
int c;
debug_printk(("resetting client %d port %d\n", mdev->client, mdev->port));
memset(&ev, 0, sizeof(ev));
ev.dest.client = mdev->client;
ev.dest.port = mdev->port;
ev.queue = dp->queue;
ev.source.port = dp->port;
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) {
ev.type = SNDRV_SEQ_EVENT_SENSING;
snd_seq_oss_dispatch(dp, &ev, 0, 0);
}
for (c = 0; c < 16; c++) {
ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
ev.data.control.channel = c;
ev.data.control.param = MIDI_CTL_ALL_NOTES_OFF;
snd_seq_oss_dispatch(dp, &ev, 0, 0);
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) {
ev.data.control.param =
MIDI_CTL_RESET_CONTROLLERS;
snd_seq_oss_dispatch(dp, &ev, 0, 0);
ev.type = SNDRV_SEQ_EVENT_PITCHBEND;
ev.data.control.value = 0;
snd_seq_oss_dispatch(dp, &ev, 0, 0);
}
}
}
// snd_seq_oss_midi_close(dp, dev);
snd_use_lock_free(&mdev->use_lock);
}
/*
* get client/port of the specified MIDI device
*/
void
snd_seq_oss_midi_get_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_addr *addr)
{
struct seq_oss_midi *mdev;
if ((mdev = get_mididev(dp, dev)) == NULL)
return;
addr->client = mdev->client;
addr->port = mdev->port;
snd_use_lock_free(&mdev->use_lock);
}
/*
* input callback - this can be atomic
*/
int
snd_seq_oss_midi_input(struct snd_seq_event *ev, int direct, void *private_data)
{
struct seq_oss_devinfo *dp = (struct seq_oss_devinfo *)private_data;
struct seq_oss_midi *mdev;
int rc;
if (dp->readq == NULL)
return 0;
if ((mdev = find_slot(ev->source.client, ev->source.port)) == NULL)
return 0;
if (! (mdev->opened & PERM_READ)) {
snd_use_lock_free(&mdev->use_lock);
return 0;
}
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
rc = send_synth_event(dp, ev, mdev->seq_device);
else
rc = send_midi_event(dp, ev, mdev);
snd_use_lock_free(&mdev->use_lock);
return rc;
}
/*
* convert ALSA sequencer event to OSS synth event
*/
static int
send_synth_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, int dev)
{
union evrec ossev;
memset(&ossev, 0, sizeof(ossev));
switch (ev->type) {
case SNDRV_SEQ_EVENT_NOTEON:
ossev.v.cmd = MIDI_NOTEON; break;
case SNDRV_SEQ_EVENT_NOTEOFF:
ossev.v.cmd = MIDI_NOTEOFF; break;
case SNDRV_SEQ_EVENT_KEYPRESS:
ossev.v.cmd = MIDI_KEY_PRESSURE; break;
case SNDRV_SEQ_EVENT_CONTROLLER:
ossev.l.cmd = MIDI_CTL_CHANGE; break;
case SNDRV_SEQ_EVENT_PGMCHANGE:
ossev.l.cmd = MIDI_PGM_CHANGE; break;
case SNDRV_SEQ_EVENT_CHANPRESS:
ossev.l.cmd = MIDI_CHN_PRESSURE; break;
case SNDRV_SEQ_EVENT_PITCHBEND:
ossev.l.cmd = MIDI_PITCH_BEND; break;
default:
return 0; /* not supported */
}
ossev.v.dev = dev;
switch (ev->type) {
case SNDRV_SEQ_EVENT_NOTEON:
case SNDRV_SEQ_EVENT_NOTEOFF:
case SNDRV_SEQ_EVENT_KEYPRESS:
ossev.v.code = EV_CHN_VOICE;
ossev.v.note = ev->data.note.note;
ossev.v.parm = ev->data.note.velocity;
ossev.v.chn = ev->data.note.channel;
break;
case SNDRV_SEQ_EVENT_CONTROLLER:
case SNDRV_SEQ_EVENT_PGMCHANGE:
case SNDRV_SEQ_EVENT_CHANPRESS:
ossev.l.code = EV_CHN_COMMON;
ossev.l.p1 = ev->data.control.param;
ossev.l.val = ev->data.control.value;
ossev.l.chn = ev->data.control.channel;
break;
case SNDRV_SEQ_EVENT_PITCHBEND:
ossev.l.code = EV_CHN_COMMON;
ossev.l.val = ev->data.control.value + 8192;
ossev.l.chn = ev->data.control.channel;
break;
}
snd_seq_oss_readq_put_timestamp(dp->readq, ev->time.tick, dp->seq_mode);
snd_seq_oss_readq_put_event(dp->readq, &ossev);
return 0;
}
/*
* decode event and send MIDI bytes to read queue
*/
static int
send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, struct seq_oss_midi *mdev)
{
char msg[32];
int len;
snd_seq_oss_readq_put_timestamp(dp->readq, ev->time.tick, dp->seq_mode);
if (!dp->timer->running)
len = snd_seq_oss_timer_start(dp->timer);
if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
snd_seq_oss_readq_puts(dp->readq, mdev->seq_device,
ev->data.ext.ptr, ev->data.ext.len);
} else {
len = snd_midi_event_decode(mdev->coder, msg, sizeof(msg), ev);
if (len > 0)
snd_seq_oss_readq_puts(dp->readq, mdev->seq_device, msg, len);
}
return 0;
}
/*
* dump midi data
* return 0 : enqueued
* non-zero : invalid - ignored
*/
int
snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c, struct snd_seq_event *ev)
{
struct seq_oss_midi *mdev;
if ((mdev = get_mididev(dp, dev)) == NULL)
return -ENODEV;
if (snd_midi_event_encode_byte(mdev->coder, c, ev) > 0) {
snd_seq_oss_fill_addr(dp, ev, mdev->client, mdev->port);
snd_use_lock_free(&mdev->use_lock);
return 0;
}
snd_use_lock_free(&mdev->use_lock);
return -EINVAL;
}
/*
* create OSS compatible midi_info record
*/
int
snd_seq_oss_midi_make_info(struct seq_oss_devinfo *dp, int dev, struct midi_info *inf)
{
struct seq_oss_midi *mdev;
if ((mdev = get_mididev(dp, dev)) == NULL)
return -ENXIO;
inf->device = dev;
inf->dev_type = 0; /* FIXME: ?? */
inf->capabilities = 0; /* FIXME: ?? */
strlcpy(inf->name, mdev->name, sizeof(inf->name));
snd_use_lock_free(&mdev->use_lock);
return 0;
}
#ifdef CONFIG_PROC_FS
/*
* proc interface
*/
static char *
capmode_str(int val)
{
val &= PERM_READ|PERM_WRITE;
if (val == (PERM_READ|PERM_WRITE))
return "read/write";
else if (val == PERM_READ)
return "read";
else if (val == PERM_WRITE)
return "write";
else
return "none";
}
void
snd_seq_oss_midi_info_read(struct snd_info_buffer *buf)
{
int i;
struct seq_oss_midi *mdev;
snd_iprintf(buf, "\nNumber of MIDI devices: %d\n", max_midi_devs);
for (i = 0; i < max_midi_devs; i++) {
snd_iprintf(buf, "\nmidi %d: ", i);
mdev = get_mdev(i);
if (mdev == NULL) {
snd_iprintf(buf, "*empty*\n");
continue;
}
snd_iprintf(buf, "[%s] ALSA port %d:%d\n", mdev->name,
mdev->client, mdev->port);
snd_iprintf(buf, " capability %s / opened %s\n",
capmode_str(mdev->flags),
capmode_str(mdev->opened));
snd_use_lock_free(&mdev->use_lock);
}
}
#endif /* CONFIG_PROC_FS */

View File

@@ -0,0 +1,48 @@
/*
* OSS compatible sequencer driver
*
* midi device information
*
* Copyright (C) 1998,99 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
*/
#ifndef __SEQ_OSS_MIDI_H
#define __SEQ_OSS_MIDI_H
#include "seq_oss_device.h"
#include <sound/seq_oss_legacy.h>
int snd_seq_oss_midi_lookup_ports(int client);
int snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo);
int snd_seq_oss_midi_check_exit_port(int client, int port);
void snd_seq_oss_midi_clear_all(void);
void snd_seq_oss_midi_setup(struct seq_oss_devinfo *dp);
void snd_seq_oss_midi_cleanup(struct seq_oss_devinfo *dp);
int snd_seq_oss_midi_open(struct seq_oss_devinfo *dp, int dev, int file_mode);
void snd_seq_oss_midi_open_all(struct seq_oss_devinfo *dp, int file_mode);
int snd_seq_oss_midi_close(struct seq_oss_devinfo *dp, int dev);
void snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev);
int snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c,
struct snd_seq_event *ev);
int snd_seq_oss_midi_input(struct snd_seq_event *ev, int direct, void *private);
int snd_seq_oss_midi_filemode(struct seq_oss_devinfo *dp, int dev);
int snd_seq_oss_midi_make_info(struct seq_oss_devinfo *dp, int dev, struct midi_info *inf);
void snd_seq_oss_midi_get_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_addr *addr);
#endif

View File

@@ -0,0 +1,236 @@
/*
* OSS compatible sequencer driver
*
* seq_oss_readq.c - MIDI input queue
*
* Copyright (C) 1998,99 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 "seq_oss_readq.h"
#include "seq_oss_event.h"
#include <sound/seq_oss_legacy.h>
#include "../seq_lock.h"
#include <linux/wait.h>
/*
* constants
*/
//#define SNDRV_SEQ_OSS_MAX_TIMEOUT (unsigned long)(-1)
#define SNDRV_SEQ_OSS_MAX_TIMEOUT (HZ * 3600)
/*
* prototypes
*/
/*
* create a read queue
*/
struct seq_oss_readq *
snd_seq_oss_readq_new(struct seq_oss_devinfo *dp, int maxlen)
{
struct seq_oss_readq *q;
if ((q = kzalloc(sizeof(*q), GFP_KERNEL)) == NULL) {
snd_printk(KERN_ERR "can't malloc read queue\n");
return NULL;
}
if ((q->q = kcalloc(maxlen, sizeof(union evrec), GFP_KERNEL)) == NULL) {
snd_printk(KERN_ERR "can't malloc read queue buffer\n");
kfree(q);
return NULL;
}
q->maxlen = maxlen;
q->qlen = 0;
q->head = q->tail = 0;
init_waitqueue_head(&q->midi_sleep);
spin_lock_init(&q->lock);
q->pre_event_timeout = SNDRV_SEQ_OSS_MAX_TIMEOUT;
q->input_time = (unsigned long)-1;
return q;
}
/*
* delete the read queue
*/
void
snd_seq_oss_readq_delete(struct seq_oss_readq *q)
{
if (q) {
kfree(q->q);
kfree(q);
}
}
/*
* reset the read queue
*/
void
snd_seq_oss_readq_clear(struct seq_oss_readq *q)
{
if (q->qlen) {
q->qlen = 0;
q->head = q->tail = 0;
}
/* if someone sleeping, wake'em up */
if (waitqueue_active(&q->midi_sleep))
wake_up(&q->midi_sleep);
q->input_time = (unsigned long)-1;
}
/*
* put a midi byte
*/
int
snd_seq_oss_readq_puts(struct seq_oss_readq *q, int dev, unsigned char *data, int len)
{
union evrec rec;
int result;
memset(&rec, 0, sizeof(rec));
rec.c[0] = SEQ_MIDIPUTC;
rec.c[2] = dev;
while (len-- > 0) {
rec.c[1] = *data++;
result = snd_seq_oss_readq_put_event(q, &rec);
if (result < 0)
return result;
}
return 0;
}
/*
* copy an event to input queue:
* return zero if enqueued
*/
int
snd_seq_oss_readq_put_event(struct seq_oss_readq *q, union evrec *ev)
{
unsigned long flags;
spin_lock_irqsave(&q->lock, flags);
if (q->qlen >= q->maxlen - 1) {
spin_unlock_irqrestore(&q->lock, flags);
return -ENOMEM;
}
memcpy(&q->q[q->tail], ev, sizeof(*ev));
q->tail = (q->tail + 1) % q->maxlen;
q->qlen++;
/* wake up sleeper */
if (waitqueue_active(&q->midi_sleep))
wake_up(&q->midi_sleep);
spin_unlock_irqrestore(&q->lock, flags);
return 0;
}
/*
* pop queue
* caller must hold lock
*/
int
snd_seq_oss_readq_pick(struct seq_oss_readq *q, union evrec *rec)
{
if (q->qlen == 0)
return -EAGAIN;
memcpy(rec, &q->q[q->head], sizeof(*rec));
return 0;
}
/*
* sleep until ready
*/
void
snd_seq_oss_readq_wait(struct seq_oss_readq *q)
{
wait_event_interruptible_timeout(q->midi_sleep,
(q->qlen > 0 || q->head == q->tail),
q->pre_event_timeout);
}
/*
* drain one record
* caller must hold lock
*/
void
snd_seq_oss_readq_free(struct seq_oss_readq *q)
{
if (q->qlen > 0) {
q->head = (q->head + 1) % q->maxlen;
q->qlen--;
}
}
/*
* polling/select:
* return non-zero if readq is not empty.
*/
unsigned int
snd_seq_oss_readq_poll(struct seq_oss_readq *q, struct file *file, poll_table *wait)
{
poll_wait(file, &q->midi_sleep, wait);
return q->qlen;
}
/*
* put a timestamp
*/
int
snd_seq_oss_readq_put_timestamp(struct seq_oss_readq *q, unsigned long curt, int seq_mode)
{
if (curt != q->input_time) {
union evrec rec;
memset(&rec, 0, sizeof(rec));
switch (seq_mode) {
case SNDRV_SEQ_OSS_MODE_SYNTH:
rec.echo = (curt << 8) | SEQ_WAIT;
snd_seq_oss_readq_put_event(q, &rec);
break;
case SNDRV_SEQ_OSS_MODE_MUSIC:
rec.t.code = EV_TIMING;
rec.t.cmd = TMR_WAIT_ABS;
rec.t.time = curt;
snd_seq_oss_readq_put_event(q, &rec);
break;
}
q->input_time = curt;
}
return 0;
}
#ifdef CONFIG_PROC_FS
/*
* proc interface
*/
void
snd_seq_oss_readq_info_read(struct seq_oss_readq *q, struct snd_info_buffer *buf)
{
snd_iprintf(buf, " read queue [%s] length = %d : tick = %ld\n",
(waitqueue_active(&q->midi_sleep) ? "sleeping":"running"),
q->qlen, q->input_time);
}
#endif /* CONFIG_PROC_FS */

View File

@@ -0,0 +1,56 @@
/*
* OSS compatible sequencer driver
* read fifo queue
*
* Copyright (C) 1998,99 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
*/
#ifndef __SEQ_OSS_READQ_H
#define __SEQ_OSS_READQ_H
#include "seq_oss_device.h"
/*
* definition of read queue
*/
struct seq_oss_readq {
union evrec *q;
int qlen;
int maxlen;
int head, tail;
unsigned long pre_event_timeout;
unsigned long input_time;
wait_queue_head_t midi_sleep;
spinlock_t lock;
};
struct seq_oss_readq *snd_seq_oss_readq_new(struct seq_oss_devinfo *dp, int maxlen);
void snd_seq_oss_readq_delete(struct seq_oss_readq *q);
void snd_seq_oss_readq_clear(struct seq_oss_readq *readq);
unsigned int snd_seq_oss_readq_poll(struct seq_oss_readq *readq, struct file *file, poll_table *wait);
int snd_seq_oss_readq_puts(struct seq_oss_readq *readq, int dev, unsigned char *data, int len);
int snd_seq_oss_readq_put_event(struct seq_oss_readq *readq, union evrec *ev);
int snd_seq_oss_readq_put_timestamp(struct seq_oss_readq *readq, unsigned long curt, int seq_mode);
int snd_seq_oss_readq_pick(struct seq_oss_readq *q, union evrec *rec);
void snd_seq_oss_readq_wait(struct seq_oss_readq *q);
void snd_seq_oss_readq_free(struct seq_oss_readq *q);
#define snd_seq_oss_readq_lock(q, flags) spin_lock_irqsave(&(q)->lock, flags)
#define snd_seq_oss_readq_unlock(q, flags) spin_unlock_irqrestore(&(q)->lock, flags)
#endif

View File

@@ -0,0 +1,216 @@
/*
* OSS compatible sequencer driver
*
* read/write/select interface to device file
*
* Copyright (C) 1998,99 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 "seq_oss_device.h"
#include "seq_oss_readq.h"
#include "seq_oss_writeq.h"
#include "seq_oss_synth.h"
#include <sound/seq_oss_legacy.h>
#include "seq_oss_event.h"
#include "seq_oss_timer.h"
#include "../seq_clientmgr.h"
/*
* protoypes
*/
static int insert_queue(struct seq_oss_devinfo *dp, union evrec *rec, struct file *opt);
/*
* read interface
*/
int
snd_seq_oss_read(struct seq_oss_devinfo *dp, char __user *buf, int count)
{
struct seq_oss_readq *readq = dp->readq;
int result = 0, err = 0;
int ev_len;
union evrec rec;
unsigned long flags;
if (readq == NULL || ! is_read_mode(dp->file_mode))
return -ENXIO;
while (count >= SHORT_EVENT_SIZE) {
snd_seq_oss_readq_lock(readq, flags);
err = snd_seq_oss_readq_pick(readq, &rec);
if (err == -EAGAIN &&
!is_nonblock_mode(dp->file_mode) && result == 0) {
snd_seq_oss_readq_unlock(readq, flags);
snd_seq_oss_readq_wait(readq);
snd_seq_oss_readq_lock(readq, flags);
if (signal_pending(current))
err = -ERESTARTSYS;
else
err = snd_seq_oss_readq_pick(readq, &rec);
}
if (err < 0) {
snd_seq_oss_readq_unlock(readq, flags);
break;
}
ev_len = ev_length(&rec);
if (ev_len < count) {
snd_seq_oss_readq_unlock(readq, flags);
break;
}
snd_seq_oss_readq_free(readq);
snd_seq_oss_readq_unlock(readq, flags);
if (copy_to_user(buf, &rec, ev_len)) {
err = -EFAULT;
break;
}
result += ev_len;
buf += ev_len;
count -= ev_len;
}
return result > 0 ? result : err;
}
/*
* write interface
*/
int
snd_seq_oss_write(struct seq_oss_devinfo *dp, const char __user *buf, int count, struct file *opt)
{
int result = 0, err = 0;
int ev_size, fmt;
union evrec rec;
if (! is_write_mode(dp->file_mode) || dp->writeq == NULL)
return -ENXIO;
while (count >= SHORT_EVENT_SIZE) {
if (copy_from_user(&rec, buf, SHORT_EVENT_SIZE)) {
err = -EFAULT;
break;
}
if (rec.s.code == SEQ_FULLSIZE) {
/* load patch */
if (result > 0) {
err = -EINVAL;
break;
}
fmt = (*(unsigned short *)rec.c) & 0xffff;
/* FIXME the return value isn't correct */
return snd_seq_oss_synth_load_patch(dp, rec.s.dev,
fmt, buf, 0, count);
}
if (ev_is_long(&rec)) {
/* extended code */
if (rec.s.code == SEQ_EXTENDED &&
dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) {
err = -EINVAL;
break;
}
ev_size = LONG_EVENT_SIZE;
if (count < ev_size)
break;
/* copy the reset 4 bytes */
if (copy_from_user(rec.c + SHORT_EVENT_SIZE,
buf + SHORT_EVENT_SIZE,
LONG_EVENT_SIZE - SHORT_EVENT_SIZE)) {
err = -EFAULT;
break;
}
} else {
/* old-type code */
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) {
err = -EINVAL;
break;
}
ev_size = SHORT_EVENT_SIZE;
}
/* insert queue */
if ((err = insert_queue(dp, &rec, opt)) < 0)
break;
result += ev_size;
buf += ev_size;
count -= ev_size;
}
return result > 0 ? result : err;
}
/*
* insert event record to write queue
* return: 0 = OK, non-zero = NG
*/
static int
insert_queue(struct seq_oss_devinfo *dp, union evrec *rec, struct file *opt)
{
int rc = 0;
struct snd_seq_event event;
/* if this is a timing event, process the current time */
if (snd_seq_oss_process_timer_event(dp->timer, rec))
return 0; /* no need to insert queue */
/* parse this event */
memset(&event, 0, sizeof(event));
/* set dummy -- to be sure */
event.type = SNDRV_SEQ_EVENT_NOTEOFF;
snd_seq_oss_fill_addr(dp, &event, dp->addr.port, dp->addr.client);
if (snd_seq_oss_process_event(dp, rec, &event))
return 0; /* invalid event - no need to insert queue */
event.time.tick = snd_seq_oss_timer_cur_tick(dp->timer);
if (dp->timer->realtime || !dp->timer->running) {
snd_seq_oss_dispatch(dp, &event, 0, 0);
} else {
if (is_nonblock_mode(dp->file_mode))
rc = snd_seq_kernel_client_enqueue(dp->cseq, &event, 0, 0);
else
rc = snd_seq_kernel_client_enqueue_blocking(dp->cseq, &event, opt, 0, 0);
}
return rc;
}
/*
* select / poll
*/
unsigned int
snd_seq_oss_poll(struct seq_oss_devinfo *dp, struct file *file, poll_table * wait)
{
unsigned int mask = 0;
/* input */
if (dp->readq && is_read_mode(dp->file_mode)) {
if (snd_seq_oss_readq_poll(dp->readq, file, wait))
mask |= POLLIN | POLLRDNORM;
}
/* output */
if (dp->writeq && is_write_mode(dp->file_mode)) {
if (snd_seq_kernel_client_write_poll(dp->cseq, file, wait))
mask |= POLLOUT | POLLWRNORM;
}
return mask;
}

View File

@@ -0,0 +1,662 @@
/*
* OSS compatible sequencer driver
*
* synth device handlers
*
* Copyright (C) 1998,99 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 "seq_oss_synth.h"
#include "seq_oss_midi.h"
#include "../seq_lock.h"
#include <linux/init.h>
/*
* constants
*/
#define SNDRV_SEQ_OSS_MAX_SYNTH_NAME 30
#define MAX_SYSEX_BUFLEN 128
/*
* definition of synth info records
*/
/* sysex buffer */
struct seq_oss_synth_sysex {
int len;
int skip;
unsigned char buf[MAX_SYSEX_BUFLEN];
};
/* synth info */
struct seq_oss_synth {
int seq_device;
/* for synth_info */
int synth_type;
int synth_subtype;
int nr_voices;
char name[SNDRV_SEQ_OSS_MAX_SYNTH_NAME];
struct snd_seq_oss_callback oper;
int opened;
void *private_data;
snd_use_lock_t use_lock;
};
/*
* device table
*/
static int max_synth_devs;
static struct seq_oss_synth *synth_devs[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS];
static struct seq_oss_synth midi_synth_dev = {
-1, /* seq_device */
SYNTH_TYPE_MIDI, /* synth_type */
0, /* synth_subtype */
16, /* nr_voices */
"MIDI", /* name */
};
static DEFINE_SPINLOCK(register_lock);
/*
* prototypes
*/
static struct seq_oss_synth *get_synthdev(struct seq_oss_devinfo *dp, int dev);
static void reset_channels(struct seq_oss_synthinfo *info);
/*
* global initialization
*/
void __init
snd_seq_oss_synth_init(void)
{
snd_use_lock_init(&midi_synth_dev.use_lock);
}
/*
* registration of the synth device
*/
int
snd_seq_oss_synth_register(struct snd_seq_device *dev)
{
int i;
struct seq_oss_synth *rec;
struct snd_seq_oss_reg *reg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
unsigned long flags;
if ((rec = kzalloc(sizeof(*rec), GFP_KERNEL)) == NULL) {
snd_printk(KERN_ERR "can't malloc synth info\n");
return -ENOMEM;
}
rec->seq_device = -1;
rec->synth_type = reg->type;
rec->synth_subtype = reg->subtype;
rec->nr_voices = reg->nvoices;
rec->oper = reg->oper;
rec->private_data = reg->private_data;
rec->opened = 0;
snd_use_lock_init(&rec->use_lock);
/* copy and truncate the name of synth device */
strlcpy(rec->name, dev->name, sizeof(rec->name));
/* registration */
spin_lock_irqsave(&register_lock, flags);
for (i = 0; i < max_synth_devs; i++) {
if (synth_devs[i] == NULL)
break;
}
if (i >= max_synth_devs) {
if (max_synth_devs >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) {
spin_unlock_irqrestore(&register_lock, flags);
snd_printk(KERN_ERR "no more synth slot\n");
kfree(rec);
return -ENOMEM;
}
max_synth_devs++;
}
rec->seq_device = i;
synth_devs[i] = rec;
debug_printk(("synth %s registered %d\n", rec->name, i));
spin_unlock_irqrestore(&register_lock, flags);
dev->driver_data = rec;
#ifdef SNDRV_OSS_INFO_DEV_SYNTH
if (i < SNDRV_CARDS)
snd_oss_info_register(SNDRV_OSS_INFO_DEV_SYNTH, i, rec->name);
#endif
return 0;
}
int
snd_seq_oss_synth_unregister(struct snd_seq_device *dev)
{
int index;
struct seq_oss_synth *rec = dev->driver_data;
unsigned long flags;
spin_lock_irqsave(&register_lock, flags);
for (index = 0; index < max_synth_devs; index++) {
if (synth_devs[index] == rec)
break;
}
if (index >= max_synth_devs) {
spin_unlock_irqrestore(&register_lock, flags);
snd_printk(KERN_ERR "can't unregister synth\n");
return -EINVAL;
}
synth_devs[index] = NULL;
if (index == max_synth_devs - 1) {
for (index--; index >= 0; index--) {
if (synth_devs[index])
break;
}
max_synth_devs = index + 1;
}
spin_unlock_irqrestore(&register_lock, flags);
#ifdef SNDRV_OSS_INFO_DEV_SYNTH
if (rec->seq_device < SNDRV_CARDS)
snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_SYNTH, rec->seq_device);
#endif
snd_use_lock_sync(&rec->use_lock);
kfree(rec);
return 0;
}
/*
*/
static struct seq_oss_synth *
get_sdev(int dev)
{
struct seq_oss_synth *rec;
unsigned long flags;
spin_lock_irqsave(&register_lock, flags);
rec = synth_devs[dev];
if (rec)
snd_use_lock_use(&rec->use_lock);
spin_unlock_irqrestore(&register_lock, flags);
return rec;
}
/*
* set up synth tables
*/
void
snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp)
{
int i;
struct seq_oss_synth *rec;
struct seq_oss_synthinfo *info;
dp->max_synthdev = max_synth_devs;
dp->synth_opened = 0;
memset(dp->synths, 0, sizeof(dp->synths));
for (i = 0; i < dp->max_synthdev; i++) {
rec = get_sdev(i);
if (rec == NULL)
continue;
if (rec->oper.open == NULL || rec->oper.close == NULL) {
snd_use_lock_free(&rec->use_lock);
continue;
}
info = &dp->synths[i];
info->arg.app_index = dp->port;
info->arg.file_mode = dp->file_mode;
info->arg.seq_mode = dp->seq_mode;
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH)
info->arg.event_passing = SNDRV_SEQ_OSS_PROCESS_EVENTS;
else
info->arg.event_passing = SNDRV_SEQ_OSS_PASS_EVENTS;
info->opened = 0;
if (!try_module_get(rec->oper.owner)) {
snd_use_lock_free(&rec->use_lock);
continue;
}
if (rec->oper.open(&info->arg, rec->private_data) < 0) {
module_put(rec->oper.owner);
snd_use_lock_free(&rec->use_lock);
continue;
}
info->nr_voices = rec->nr_voices;
if (info->nr_voices > 0) {
info->ch = kcalloc(info->nr_voices, sizeof(struct seq_oss_chinfo), GFP_KERNEL);
if (!info->ch) {
snd_printk(KERN_ERR "Cannot malloc\n");
rec->oper.close(&info->arg);
module_put(rec->oper.owner);
snd_use_lock_free(&rec->use_lock);
continue;
}
reset_channels(info);
}
debug_printk(("synth %d assigned\n", i));
info->opened++;
rec->opened++;
dp->synth_opened++;
snd_use_lock_free(&rec->use_lock);
}
}
/*
* set up synth tables for MIDI emulation - /dev/music mode only
*/
void
snd_seq_oss_synth_setup_midi(struct seq_oss_devinfo *dp)
{
int i;
if (dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS)
return;
for (i = 0; i < dp->max_mididev; i++) {
struct seq_oss_synthinfo *info;
info = &dp->synths[dp->max_synthdev];
if (snd_seq_oss_midi_open(dp, i, dp->file_mode) < 0)
continue;
info->arg.app_index = dp->port;
info->arg.file_mode = dp->file_mode;
info->arg.seq_mode = dp->seq_mode;
info->arg.private_data = info;
info->is_midi = 1;
info->midi_mapped = i;
info->arg.event_passing = SNDRV_SEQ_OSS_PASS_EVENTS;
snd_seq_oss_midi_get_addr(dp, i, &info->arg.addr);
info->opened = 1;
midi_synth_dev.opened++;
dp->max_synthdev++;
if (dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS)
break;
}
}
/*
* clean up synth tables
*/
void
snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp)
{
int i;
struct seq_oss_synth *rec;
struct seq_oss_synthinfo *info;
if (snd_BUG_ON(dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS))
return;
for (i = 0; i < dp->max_synthdev; i++) {
info = &dp->synths[i];
if (! info->opened)
continue;
if (info->is_midi) {
if (midi_synth_dev.opened > 0) {
snd_seq_oss_midi_close(dp, info->midi_mapped);
midi_synth_dev.opened--;
}
} else {
rec = get_sdev(i);
if (rec == NULL)
continue;
if (rec->opened > 0) {
debug_printk(("synth %d closed\n", i));
rec->oper.close(&info->arg);
module_put(rec->oper.owner);
rec->opened = 0;
}
snd_use_lock_free(&rec->use_lock);
}
kfree(info->sysex);
info->sysex = NULL;
kfree(info->ch);
info->ch = NULL;
}
dp->synth_opened = 0;
dp->max_synthdev = 0;
}
/*
* check if the specified device is MIDI mapped device
*/
static int
is_midi_dev(struct seq_oss_devinfo *dp, int dev)
{
if (dev < 0 || dev >= dp->max_synthdev)
return 0;
if (dp->synths[dev].is_midi)
return 1;
return 0;
}
/*
* return synth device information pointer
*/
static struct seq_oss_synth *
get_synthdev(struct seq_oss_devinfo *dp, int dev)
{
struct seq_oss_synth *rec;
if (dev < 0 || dev >= dp->max_synthdev)
return NULL;
if (! dp->synths[dev].opened)
return NULL;
if (dp->synths[dev].is_midi)
return &midi_synth_dev;
if ((rec = get_sdev(dev)) == NULL)
return NULL;
if (! rec->opened) {
snd_use_lock_free(&rec->use_lock);
return NULL;
}
return rec;
}
/*
* reset note and velocity on each channel.
*/
static void
reset_channels(struct seq_oss_synthinfo *info)
{
int i;
if (info->ch == NULL || ! info->nr_voices)
return;
for (i = 0; i < info->nr_voices; i++) {
info->ch[i].note = -1;
info->ch[i].vel = 0;
}
}
/*
* reset synth device:
* call reset callback. if no callback is defined, send a heartbeat
* event to the corresponding port.
*/
void
snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev)
{
struct seq_oss_synth *rec;
struct seq_oss_synthinfo *info;
if (snd_BUG_ON(dev < 0 || dev >= dp->max_synthdev))
return;
info = &dp->synths[dev];
if (! info->opened)
return;
if (info->sysex)
info->sysex->len = 0; /* reset sysex */
reset_channels(info);
if (info->is_midi) {
if (midi_synth_dev.opened <= 0)
return;
snd_seq_oss_midi_reset(dp, info->midi_mapped);
/* reopen the device */
snd_seq_oss_midi_close(dp, dev);
if (snd_seq_oss_midi_open(dp, info->midi_mapped,
dp->file_mode) < 0) {
midi_synth_dev.opened--;
info->opened = 0;
kfree(info->sysex);
info->sysex = NULL;
kfree(info->ch);
info->ch = NULL;
}
return;
}
rec = get_sdev(dev);
if (rec == NULL)
return;
if (rec->oper.reset) {
rec->oper.reset(&info->arg);
} else {
struct snd_seq_event ev;
memset(&ev, 0, sizeof(ev));
snd_seq_oss_fill_addr(dp, &ev, info->arg.addr.client,
info->arg.addr.port);
ev.type = SNDRV_SEQ_EVENT_RESET;
snd_seq_oss_dispatch(dp, &ev, 0, 0);
}
snd_use_lock_free(&rec->use_lock);
}
/*
* load a patch record:
* call load_patch callback function
*/
int
snd_seq_oss_synth_load_patch(struct seq_oss_devinfo *dp, int dev, int fmt,
const char __user *buf, int p, int c)
{
struct seq_oss_synth *rec;
int rc;
if (dev < 0 || dev >= dp->max_synthdev)
return -ENXIO;
if (is_midi_dev(dp, dev))
return 0;
if ((rec = get_synthdev(dp, dev)) == NULL)
return -ENXIO;
if (rec->oper.load_patch == NULL)
rc = -ENXIO;
else
rc = rec->oper.load_patch(&dp->synths[dev].arg, fmt, buf, p, c);
snd_use_lock_free(&rec->use_lock);
return rc;
}
/*
* check if the device is valid synth device
*/
int
snd_seq_oss_synth_is_valid(struct seq_oss_devinfo *dp, int dev)
{
struct seq_oss_synth *rec;
rec = get_synthdev(dp, dev);
if (rec) {
snd_use_lock_free(&rec->use_lock);
return 1;
}
return 0;
}
/*
* receive OSS 6 byte sysex packet:
* the full sysex message will be sent if it reaches to the end of data
* (0xff).
*/
int
snd_seq_oss_synth_sysex(struct seq_oss_devinfo *dp, int dev, unsigned char *buf, struct snd_seq_event *ev)
{
int i, send;
unsigned char *dest;
struct seq_oss_synth_sysex *sysex;
if (! snd_seq_oss_synth_is_valid(dp, dev))
return -ENXIO;
sysex = dp->synths[dev].sysex;
if (sysex == NULL) {
sysex = kzalloc(sizeof(*sysex), GFP_KERNEL);
if (sysex == NULL)
return -ENOMEM;
dp->synths[dev].sysex = sysex;
}
send = 0;
dest = sysex->buf + sysex->len;
/* copy 6 byte packet to the buffer */
for (i = 0; i < 6; i++) {
if (buf[i] == 0xff) {
send = 1;
break;
}
dest[i] = buf[i];
sysex->len++;
if (sysex->len >= MAX_SYSEX_BUFLEN) {
sysex->len = 0;
sysex->skip = 1;
break;
}
}
if (sysex->len && send) {
if (sysex->skip) {
sysex->skip = 0;
sysex->len = 0;
return -EINVAL; /* skip */
}
/* copy the data to event record and send it */
ev->flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE;
if (snd_seq_oss_synth_addr(dp, dev, ev))
return -EINVAL;
ev->data.ext.len = sysex->len;
ev->data.ext.ptr = sysex->buf;
sysex->len = 0;
return 0;
}
return -EINVAL; /* skip */
}
/*
* fill the event source/destination addresses
*/
int
snd_seq_oss_synth_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_event *ev)
{
if (! snd_seq_oss_synth_is_valid(dp, dev))
return -EINVAL;
snd_seq_oss_fill_addr(dp, ev, dp->synths[dev].arg.addr.client,
dp->synths[dev].arg.addr.port);
return 0;
}
/*
* OSS compatible ioctl
*/
int
snd_seq_oss_synth_ioctl(struct seq_oss_devinfo *dp, int dev, unsigned int cmd, unsigned long addr)
{
struct seq_oss_synth *rec;
int rc;
if (is_midi_dev(dp, dev))
return -ENXIO;
if ((rec = get_synthdev(dp, dev)) == NULL)
return -ENXIO;
if (rec->oper.ioctl == NULL)
rc = -ENXIO;
else
rc = rec->oper.ioctl(&dp->synths[dev].arg, cmd, addr);
snd_use_lock_free(&rec->use_lock);
return rc;
}
/*
* send OSS raw events - SEQ_PRIVATE and SEQ_VOLUME
*/
int
snd_seq_oss_synth_raw_event(struct seq_oss_devinfo *dp, int dev, unsigned char *data, struct snd_seq_event *ev)
{
if (! snd_seq_oss_synth_is_valid(dp, dev) || is_midi_dev(dp, dev))
return -ENXIO;
ev->type = SNDRV_SEQ_EVENT_OSS;
memcpy(ev->data.raw8.d, data, 8);
return snd_seq_oss_synth_addr(dp, dev, ev);
}
/*
* create OSS compatible synth_info record
*/
int
snd_seq_oss_synth_make_info(struct seq_oss_devinfo *dp, int dev, struct synth_info *inf)
{
struct seq_oss_synth *rec;
if (dev < 0 || dev >= dp->max_synthdev)
return -ENXIO;
if (dp->synths[dev].is_midi) {
struct midi_info minf;
snd_seq_oss_midi_make_info(dp, dp->synths[dev].midi_mapped, &minf);
inf->synth_type = SYNTH_TYPE_MIDI;
inf->synth_subtype = 0;
inf->nr_voices = 16;
inf->device = dev;
strlcpy(inf->name, minf.name, sizeof(inf->name));
} else {
if ((rec = get_synthdev(dp, dev)) == NULL)
return -ENXIO;
inf->synth_type = rec->synth_type;
inf->synth_subtype = rec->synth_subtype;
inf->nr_voices = rec->nr_voices;
inf->device = dev;
strlcpy(inf->name, rec->name, sizeof(inf->name));
snd_use_lock_free(&rec->use_lock);
}
return 0;
}
#ifdef CONFIG_PROC_FS
/*
* proc interface
*/
void
snd_seq_oss_synth_info_read(struct snd_info_buffer *buf)
{
int i;
struct seq_oss_synth *rec;
snd_iprintf(buf, "\nNumber of synth devices: %d\n", max_synth_devs);
for (i = 0; i < max_synth_devs; i++) {
snd_iprintf(buf, "\nsynth %d: ", i);
rec = get_sdev(i);
if (rec == NULL) {
snd_iprintf(buf, "*empty*\n");
continue;
}
snd_iprintf(buf, "[%s]\n", rec->name);
snd_iprintf(buf, " type 0x%x : subtype 0x%x : voices %d\n",
rec->synth_type, rec->synth_subtype,
rec->nr_voices);
snd_iprintf(buf, " capabilities : ioctl %s / load_patch %s\n",
enabled_str((long)rec->oper.ioctl),
enabled_str((long)rec->oper.load_patch));
snd_use_lock_free(&rec->use_lock);
}
}
#endif /* CONFIG_PROC_FS */

View File

@@ -0,0 +1,51 @@
/*
* OSS compatible sequencer driver
*
* synth device information
*
* Copyright (C) 1998,99 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
*/
#ifndef __SEQ_OSS_SYNTH_H
#define __SEQ_OSS_SYNTH_H
#include "seq_oss_device.h"
#include <sound/seq_oss_legacy.h>
#include <sound/seq_device.h>
void snd_seq_oss_synth_init(void);
int snd_seq_oss_synth_register(struct snd_seq_device *dev);
int snd_seq_oss_synth_unregister(struct snd_seq_device *dev);
void snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp);
void snd_seq_oss_synth_setup_midi(struct seq_oss_devinfo *dp);
void snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp);
void snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev);
int snd_seq_oss_synth_load_patch(struct seq_oss_devinfo *dp, int dev, int fmt,
const char __user *buf, int p, int c);
int snd_seq_oss_synth_is_valid(struct seq_oss_devinfo *dp, int dev);
int snd_seq_oss_synth_sysex(struct seq_oss_devinfo *dp, int dev, unsigned char *buf,
struct snd_seq_event *ev);
int snd_seq_oss_synth_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_event *ev);
int snd_seq_oss_synth_ioctl(struct seq_oss_devinfo *dp, int dev, unsigned int cmd,
unsigned long addr);
int snd_seq_oss_synth_raw_event(struct seq_oss_devinfo *dp, int dev,
unsigned char *data, struct snd_seq_event *ev);
int snd_seq_oss_synth_make_info(struct seq_oss_devinfo *dp, int dev, struct synth_info *inf);
#endif

View File

@@ -0,0 +1,283 @@
/*
* OSS compatible sequencer driver
*
* Timer control routines
*
* Copyright (C) 1998,99 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 "seq_oss_timer.h"
#include "seq_oss_event.h"
#include <sound/seq_oss_legacy.h>
/*
*/
#define MIN_OSS_TEMPO 8
#define MAX_OSS_TEMPO 360
#define MIN_OSS_TIMEBASE 1
#define MAX_OSS_TIMEBASE 1000
/*
*/
static void calc_alsa_tempo(struct seq_oss_timer *timer);
static int send_timer_event(struct seq_oss_devinfo *dp, int type, int value);
/*
* create and register a new timer.
* if queue is not started yet, start it.
*/
struct seq_oss_timer *
snd_seq_oss_timer_new(struct seq_oss_devinfo *dp)
{
struct seq_oss_timer *rec;
rec = kzalloc(sizeof(*rec), GFP_KERNEL);
if (rec == NULL)
return NULL;
rec->dp = dp;
rec->cur_tick = 0;
rec->realtime = 0;
rec->running = 0;
rec->oss_tempo = 60;
rec->oss_timebase = 100;
calc_alsa_tempo(rec);
return rec;
}
/*
* delete timer.
* if no more timer exists, stop the queue.
*/
void
snd_seq_oss_timer_delete(struct seq_oss_timer *rec)
{
if (rec) {
snd_seq_oss_timer_stop(rec);
kfree(rec);
}
}
/*
* process one timing event
* return 1 : event proceseed -- skip this event
* 0 : not a timer event -- enqueue this event
*/
int
snd_seq_oss_process_timer_event(struct seq_oss_timer *rec, union evrec *ev)
{
abstime_t parm = ev->t.time;
if (ev->t.code == EV_TIMING) {
switch (ev->t.cmd) {
case TMR_WAIT_REL:
parm += rec->cur_tick;
rec->realtime = 0;
/* continue to next */
case TMR_WAIT_ABS:
if (parm == 0) {
rec->realtime = 1;
} else if (parm >= rec->cur_tick) {
rec->realtime = 0;
rec->cur_tick = parm;
}
return 1; /* skip this event */
case TMR_START:
snd_seq_oss_timer_start(rec);
return 1;
}
} else if (ev->s.code == SEQ_WAIT) {
/* time = from 1 to 3 bytes */
parm = (ev->echo >> 8) & 0xffffff;
if (parm > rec->cur_tick) {
/* set next event time */
rec->cur_tick = parm;
rec->realtime = 0;
}
return 1;
}
return 0;
}
/*
* convert tempo units
*/
static void
calc_alsa_tempo(struct seq_oss_timer *timer)
{
timer->tempo = (60 * 1000000) / timer->oss_tempo;
timer->ppq = timer->oss_timebase;
}
/*
* dispatch a timer event
*/
static int
send_timer_event(struct seq_oss_devinfo *dp, int type, int value)
{
struct snd_seq_event ev;
memset(&ev, 0, sizeof(ev));
ev.type = type;
ev.source.client = dp->cseq;
ev.source.port = 0;
ev.dest.client = SNDRV_SEQ_CLIENT_SYSTEM;
ev.dest.port = SNDRV_SEQ_PORT_SYSTEM_TIMER;
ev.queue = dp->queue;
ev.data.queue.queue = dp->queue;
ev.data.queue.param.value = value;
return snd_seq_kernel_client_dispatch(dp->cseq, &ev, 1, 0);
}
/*
* set queue tempo and start queue
*/
int
snd_seq_oss_timer_start(struct seq_oss_timer *timer)
{
struct seq_oss_devinfo *dp = timer->dp;
struct snd_seq_queue_tempo tmprec;
if (timer->running)
snd_seq_oss_timer_stop(timer);
memset(&tmprec, 0, sizeof(tmprec));
tmprec.queue = dp->queue;
tmprec.ppq = timer->ppq;
tmprec.tempo = timer->tempo;
snd_seq_set_queue_tempo(dp->cseq, &tmprec);
send_timer_event(dp, SNDRV_SEQ_EVENT_START, 0);
timer->running = 1;
timer->cur_tick = 0;
return 0;
}
/*
* stop queue
*/
int
snd_seq_oss_timer_stop(struct seq_oss_timer *timer)
{
if (! timer->running)
return 0;
send_timer_event(timer->dp, SNDRV_SEQ_EVENT_STOP, 0);
timer->running = 0;
return 0;
}
/*
* continue queue
*/
int
snd_seq_oss_timer_continue(struct seq_oss_timer *timer)
{
if (timer->running)
return 0;
send_timer_event(timer->dp, SNDRV_SEQ_EVENT_CONTINUE, 0);
timer->running = 1;
return 0;
}
/*
* change queue tempo
*/
int
snd_seq_oss_timer_tempo(struct seq_oss_timer *timer, int value)
{
if (value < MIN_OSS_TEMPO)
value = MIN_OSS_TEMPO;
else if (value > MAX_OSS_TEMPO)
value = MAX_OSS_TEMPO;
timer->oss_tempo = value;
calc_alsa_tempo(timer);
if (timer->running)
send_timer_event(timer->dp, SNDRV_SEQ_EVENT_TEMPO, timer->tempo);
return 0;
}
/*
* ioctls
*/
int
snd_seq_oss_timer_ioctl(struct seq_oss_timer *timer, unsigned int cmd, int __user *arg)
{
int value;
if (cmd == SNDCTL_SEQ_CTRLRATE) {
debug_printk(("ctrl rate\n"));
/* if *arg == 0, just return the current rate */
if (get_user(value, arg))
return -EFAULT;
if (value)
return -EINVAL;
value = ((timer->oss_tempo * timer->oss_timebase) + 30) / 60;
return put_user(value, arg) ? -EFAULT : 0;
}
if (timer->dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH)
return 0;
switch (cmd) {
case SNDCTL_TMR_START:
debug_printk(("timer start\n"));
return snd_seq_oss_timer_start(timer);
case SNDCTL_TMR_STOP:
debug_printk(("timer stop\n"));
return snd_seq_oss_timer_stop(timer);
case SNDCTL_TMR_CONTINUE:
debug_printk(("timer continue\n"));
return snd_seq_oss_timer_continue(timer);
case SNDCTL_TMR_TEMPO:
debug_printk(("timer tempo\n"));
if (get_user(value, arg))
return -EFAULT;
return snd_seq_oss_timer_tempo(timer, value);
case SNDCTL_TMR_TIMEBASE:
debug_printk(("timer timebase\n"));
if (get_user(value, arg))
return -EFAULT;
if (value < MIN_OSS_TIMEBASE)
value = MIN_OSS_TIMEBASE;
else if (value > MAX_OSS_TIMEBASE)
value = MAX_OSS_TIMEBASE;
timer->oss_timebase = value;
calc_alsa_tempo(timer);
return 0;
case SNDCTL_TMR_METRONOME:
case SNDCTL_TMR_SELECT:
case SNDCTL_TMR_SOURCE:
debug_printk(("timer XXX\n"));
/* not supported */
return 0;
}
return 0;
}

View File

@@ -0,0 +1,70 @@
/*
* OSS compatible sequencer driver
* timer handling routines
*
* Copyright (C) 1998,99 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
*/
#ifndef __SEQ_OSS_TIMER_H
#define __SEQ_OSS_TIMER_H
#include "seq_oss_device.h"
/*
* timer information definition
*/
struct seq_oss_timer {
struct seq_oss_devinfo *dp;
reltime_t cur_tick;
int realtime;
int running;
int tempo, ppq; /* ALSA queue */
int oss_tempo, oss_timebase;
};
struct seq_oss_timer *snd_seq_oss_timer_new(struct seq_oss_devinfo *dp);
void snd_seq_oss_timer_delete(struct seq_oss_timer *dp);
int snd_seq_oss_timer_start(struct seq_oss_timer *timer);
int snd_seq_oss_timer_stop(struct seq_oss_timer *timer);
int snd_seq_oss_timer_continue(struct seq_oss_timer *timer);
int snd_seq_oss_timer_tempo(struct seq_oss_timer *timer, int value);
#define snd_seq_oss_timer_reset snd_seq_oss_timer_start
int snd_seq_oss_timer_ioctl(struct seq_oss_timer *timer, unsigned int cmd, int __user *arg);
/*
* get current processed time
*/
static inline abstime_t
snd_seq_oss_timer_cur_tick(struct seq_oss_timer *timer)
{
return timer->cur_tick;
}
/*
* is realtime event?
*/
static inline int
snd_seq_oss_timer_is_realtime(struct seq_oss_timer *timer)
{
return timer->realtime;
}
#endif

View File

@@ -0,0 +1,172 @@
/*
* OSS compatible sequencer driver
*
* seq_oss_writeq.c - write queue and sync
*
* Copyright (C) 1998,99 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 "seq_oss_writeq.h"
#include "seq_oss_event.h"
#include "seq_oss_timer.h"
#include <sound/seq_oss_legacy.h>
#include "../seq_lock.h"
#include "../seq_clientmgr.h"
#include <linux/wait.h>
/*
* create a write queue record
*/
struct seq_oss_writeq *
snd_seq_oss_writeq_new(struct seq_oss_devinfo *dp, int maxlen)
{
struct seq_oss_writeq *q;
struct snd_seq_client_pool pool;
if ((q = kzalloc(sizeof(*q), GFP_KERNEL)) == NULL)
return NULL;
q->dp = dp;
q->maxlen = maxlen;
spin_lock_init(&q->sync_lock);
q->sync_event_put = 0;
q->sync_time = 0;
init_waitqueue_head(&q->sync_sleep);
memset(&pool, 0, sizeof(pool));
pool.client = dp->cseq;
pool.output_pool = maxlen;
pool.output_room = maxlen / 2;
snd_seq_oss_control(dp, SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, &pool);
return q;
}
/*
* delete the write queue
*/
void
snd_seq_oss_writeq_delete(struct seq_oss_writeq *q)
{
if (q) {
snd_seq_oss_writeq_clear(q); /* to be sure */
kfree(q);
}
}
/*
* reset the write queue
*/
void
snd_seq_oss_writeq_clear(struct seq_oss_writeq *q)
{
struct snd_seq_remove_events reset;
memset(&reset, 0, sizeof(reset));
reset.remove_mode = SNDRV_SEQ_REMOVE_OUTPUT; /* remove all */
snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_REMOVE_EVENTS, &reset);
/* wake up sleepers if any */
snd_seq_oss_writeq_wakeup(q, 0);
}
/*
* wait until the write buffer has enough room
*/
int
snd_seq_oss_writeq_sync(struct seq_oss_writeq *q)
{
struct seq_oss_devinfo *dp = q->dp;
abstime_t time;
time = snd_seq_oss_timer_cur_tick(dp->timer);
if (q->sync_time >= time)
return 0; /* already finished */
if (! q->sync_event_put) {
struct snd_seq_event ev;
union evrec *rec;
/* put echoback event */
memset(&ev, 0, sizeof(ev));
ev.flags = 0;
ev.type = SNDRV_SEQ_EVENT_ECHO;
ev.time.tick = time;
/* echo back to itself */
snd_seq_oss_fill_addr(dp, &ev, dp->addr.client, dp->addr.port);
rec = (union evrec *)&ev.data;
rec->t.code = SEQ_SYNCTIMER;
rec->t.time = time;
q->sync_event_put = 1;
snd_seq_kernel_client_enqueue_blocking(dp->cseq, &ev, NULL, 0, 0);
}
wait_event_interruptible_timeout(q->sync_sleep, ! q->sync_event_put, HZ);
if (signal_pending(current))
/* interrupted - return 0 to finish sync */
q->sync_event_put = 0;
if (! q->sync_event_put || q->sync_time >= time)
return 0;
return 1;
}
/*
* wake up sync - echo event was catched
*/
void
snd_seq_oss_writeq_wakeup(struct seq_oss_writeq *q, abstime_t time)
{
unsigned long flags;
spin_lock_irqsave(&q->sync_lock, flags);
q->sync_time = time;
q->sync_event_put = 0;
if (waitqueue_active(&q->sync_sleep)) {
wake_up(&q->sync_sleep);
}
spin_unlock_irqrestore(&q->sync_lock, flags);
}
/*
* return the unused pool size
*/
int
snd_seq_oss_writeq_get_free_size(struct seq_oss_writeq *q)
{
struct snd_seq_client_pool pool;
pool.client = q->dp->cseq;
snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, &pool);
return pool.output_free;
}
/*
* set output threshold size from ioctl
*/
void
snd_seq_oss_writeq_set_output(struct seq_oss_writeq *q, int val)
{
struct snd_seq_client_pool pool;
pool.client = q->dp->cseq;
snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, &pool);
pool.output_room = val;
snd_seq_oss_control(q->dp, SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, &pool);
}

View File

@@ -0,0 +1,50 @@
/*
* OSS compatible sequencer driver
* write priority queue
*
* Copyright (C) 1998,99 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
*/
#ifndef __SEQ_OSS_WRITEQ_H
#define __SEQ_OSS_WRITEQ_H
#include "seq_oss_device.h"
struct seq_oss_writeq {
struct seq_oss_devinfo *dp;
int maxlen;
abstime_t sync_time;
int sync_event_put;
wait_queue_head_t sync_sleep;
spinlock_t sync_lock;
};
/*
* seq_oss_writeq.c
*/
struct seq_oss_writeq *snd_seq_oss_writeq_new(struct seq_oss_devinfo *dp, int maxlen);
void snd_seq_oss_writeq_delete(struct seq_oss_writeq *q);
void snd_seq_oss_writeq_clear(struct seq_oss_writeq *q);
int snd_seq_oss_writeq_sync(struct seq_oss_writeq *q);
void snd_seq_oss_writeq_wakeup(struct seq_oss_writeq *q, abstime_t time);
int snd_seq_oss_writeq_get_free_size(struct seq_oss_writeq *q);
void snd_seq_oss_writeq_set_output(struct seq_oss_writeq *q, int size);
#endif