linux/sound/drivers/opl3/opl3_oss.c
<<
>>
Prefs
   1/*
   2 *  Interface for OSS sequencer emulation
   3 *
   4 *  Copyright (C) 2000 Uros Bizjak <uros@kss-loka.si>
   5 *
   6 *   This program is free software; you can redistribute it and/or modify
   7 *   it under the terms of the GNU General Public License as published by
   8 *   the Free Software Foundation; either version 2 of the License, or
   9 *   (at your option) any later version.
  10 *
  11 *   This program is distributed in the hope that it will be useful,
  12 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 *   GNU General Public License for more details.
  15 *
  16 *   You should have received a copy of the GNU General Public License
  17 *   along with this program; if not, write to the Free Software
  18 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  19 */
  20
  21#include "opl3_voice.h"
  22
  23static int snd_opl3_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure);
  24static int snd_opl3_close_seq_oss(struct snd_seq_oss_arg *arg);
  25static int snd_opl3_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd, unsigned long ioarg);
  26static int snd_opl3_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format, const char __user *buf, int offs, int count);
  27static int snd_opl3_reset_seq_oss(struct snd_seq_oss_arg *arg);
  28
  29/* */
  30
  31static inline mm_segment_t snd_enter_user(void)
  32{
  33        mm_segment_t fs = get_fs();
  34        set_fs(get_ds());
  35        return fs;
  36}
  37
  38static inline void snd_leave_user(mm_segment_t fs)
  39{
  40        set_fs(fs);
  41}
  42
  43/* operators */
  44
  45extern struct snd_midi_op opl3_ops;
  46
  47static struct snd_seq_oss_callback oss_callback = {
  48        .owner =        THIS_MODULE,
  49        .open =         snd_opl3_open_seq_oss,
  50        .close =        snd_opl3_close_seq_oss,
  51        .ioctl =        snd_opl3_ioctl_seq_oss,
  52        .load_patch =   snd_opl3_load_patch_seq_oss,
  53        .reset =        snd_opl3_reset_seq_oss,
  54};
  55
  56static int snd_opl3_oss_event_input(struct snd_seq_event *ev, int direct,
  57                                    void *private_data, int atomic, int hop)
  58{
  59        struct snd_opl3 *opl3 = private_data;
  60
  61        if (ev->type != SNDRV_SEQ_EVENT_OSS)
  62                snd_midi_process_event(&opl3_ops, ev, opl3->oss_chset);
  63        return 0;
  64}
  65
  66/* ------------------------------ */
  67
  68static void snd_opl3_oss_free_port(void *private_data)
  69{
  70        struct snd_opl3 *opl3 = private_data;
  71
  72        snd_midi_channel_free_set(opl3->oss_chset);
  73}
  74
  75static int snd_opl3_oss_create_port(struct snd_opl3 * opl3)
  76{
  77        struct snd_seq_port_callback callbacks;
  78        char name[32];
  79        int voices, opl_ver;
  80
  81        voices = (opl3->hardware < OPL3_HW_OPL3) ?
  82                MAX_OPL2_VOICES : MAX_OPL3_VOICES;
  83        opl3->oss_chset = snd_midi_channel_alloc_set(voices);
  84        if (opl3->oss_chset == NULL)
  85                return -ENOMEM;
  86        opl3->oss_chset->private_data = opl3;
  87
  88        memset(&callbacks, 0, sizeof(callbacks));
  89        callbacks.owner = THIS_MODULE;
  90        callbacks.event_input = snd_opl3_oss_event_input;
  91        callbacks.private_free = snd_opl3_oss_free_port;
  92        callbacks.private_data = opl3;
  93
  94        opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8;
  95        sprintf(name, "OPL%i OSS Port", opl_ver);
  96
  97        opl3->oss_chset->client = opl3->seq_client;
  98        opl3->oss_chset->port = snd_seq_event_port_attach(opl3->seq_client, &callbacks,
  99                                                          SNDRV_SEQ_PORT_CAP_WRITE,
 100                                                          SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
 101                                                          SNDRV_SEQ_PORT_TYPE_MIDI_GM |
 102                                                          SNDRV_SEQ_PORT_TYPE_HARDWARE |
 103                                                          SNDRV_SEQ_PORT_TYPE_SYNTHESIZER,
 104                                                          voices, voices,
 105                                                          name);
 106        if (opl3->oss_chset->port < 0) {
 107                int port;
 108                port = opl3->oss_chset->port;
 109                snd_midi_channel_free_set(opl3->oss_chset);
 110                return port;
 111        }
 112        return 0;
 113}
 114
 115/* ------------------------------ */
 116
 117/* register OSS synth */
 118void snd_opl3_init_seq_oss(struct snd_opl3 *opl3, char *name)
 119{
 120        struct snd_seq_oss_reg *arg;
 121        struct snd_seq_device *dev;
 122
 123        if (snd_seq_device_new(opl3->card, 0, SNDRV_SEQ_DEV_ID_OSS,
 124                               sizeof(struct snd_seq_oss_reg), &dev) < 0)
 125                return;
 126
 127        opl3->oss_seq_dev = dev;
 128        strlcpy(dev->name, name, sizeof(dev->name));
 129        arg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
 130        arg->type = SYNTH_TYPE_FM;
 131        if (opl3->hardware < OPL3_HW_OPL3) {
 132                arg->subtype = FM_TYPE_ADLIB;
 133                arg->nvoices = MAX_OPL2_VOICES;
 134        } else {
 135                arg->subtype = FM_TYPE_OPL3;
 136                arg->nvoices = MAX_OPL3_VOICES;
 137        }
 138        arg->oper = oss_callback;
 139        arg->private_data = opl3;
 140
 141        if (snd_opl3_oss_create_port(opl3)) {
 142                /* register to OSS synth table */
 143                snd_device_register(opl3->card, dev);
 144        }
 145}
 146
 147/* unregister */
 148void snd_opl3_free_seq_oss(struct snd_opl3 *opl3)
 149{
 150        if (opl3->oss_seq_dev) {
 151                /* The instance should have been released in prior */
 152                opl3->oss_seq_dev = NULL;
 153        }
 154}
 155
 156/* ------------------------------ */
 157
 158/* open OSS sequencer */
 159static int snd_opl3_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure)
 160{
 161        struct snd_opl3 *opl3 = closure;
 162        int err;
 163
 164        if (snd_BUG_ON(!arg))
 165                return -ENXIO;
 166
 167        if ((err = snd_opl3_synth_setup(opl3)) < 0)
 168                return err;
 169
 170        /* fill the argument data */
 171        arg->private_data = opl3;
 172        arg->addr.client = opl3->oss_chset->client;
 173        arg->addr.port = opl3->oss_chset->port;
 174
 175        if ((err = snd_opl3_synth_use_inc(opl3)) < 0)
 176                return err;
 177
 178        opl3->synth_mode = SNDRV_OPL3_MODE_SYNTH;
 179        return 0;
 180}
 181
 182/* close OSS sequencer */
 183static int snd_opl3_close_seq_oss(struct snd_seq_oss_arg *arg)
 184{
 185        struct snd_opl3 *opl3;
 186
 187        if (snd_BUG_ON(!arg))
 188                return -ENXIO;
 189        opl3 = arg->private_data;
 190
 191        snd_opl3_synth_cleanup(opl3);
 192
 193        snd_opl3_synth_use_dec(opl3);
 194        return 0;
 195}
 196
 197/* load patch */
 198
 199/* from sound_config.h */
 200#define SBFM_MAXINSTR   256
 201
 202static int snd_opl3_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format,
 203                                       const char __user *buf, int offs, int count)
 204{
 205        struct snd_opl3 *opl3;
 206        struct sbi_instrument sbi;
 207        char name[32];
 208        int err, type;
 209
 210        if (snd_BUG_ON(!arg))
 211                return -ENXIO;
 212        opl3 = arg->private_data;
 213
 214        if (format == FM_PATCH)
 215                type = FM_PATCH_OPL2;
 216        else if (format == OPL3_PATCH)
 217                type = FM_PATCH_OPL3;
 218        else
 219                return -EINVAL;
 220
 221        if (count < (int)sizeof(sbi)) {
 222                snd_printk(KERN_ERR "FM Error: Patch record too short\n");
 223                return -EINVAL;
 224        }
 225        if (copy_from_user(&sbi, buf, sizeof(sbi)))
 226                return -EFAULT;
 227
 228        if (sbi.channel < 0 || sbi.channel >= SBFM_MAXINSTR) {
 229                snd_printk(KERN_ERR "FM Error: Invalid instrument number %d\n",
 230                           sbi.channel);
 231                return -EINVAL;
 232        }
 233
 234        memset(name, 0, sizeof(name));
 235        sprintf(name, "Chan%d", sbi.channel);
 236
 237        err = snd_opl3_load_patch(opl3, sbi.channel, 127, type, name, NULL,
 238                                  sbi.operators);
 239        if (err < 0)
 240                return err;
 241
 242        return sizeof(sbi);
 243}
 244
 245/* ioctl */
 246static int snd_opl3_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd,
 247                                  unsigned long ioarg)
 248{
 249        struct snd_opl3 *opl3;
 250
 251        if (snd_BUG_ON(!arg))
 252                return -ENXIO;
 253        opl3 = arg->private_data;
 254        switch (cmd) {
 255                case SNDCTL_FM_LOAD_INSTR:
 256                        snd_printk(KERN_ERR "OPL3: "
 257                                   "Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. "
 258                                   "Fix the program.\n");
 259                        return -EINVAL;
 260
 261                case SNDCTL_SYNTH_MEMAVL:
 262                        return 0x7fffffff;
 263
 264                case SNDCTL_FM_4OP_ENABLE:
 265                        // handled automatically by OPL instrument type
 266                        return 0;
 267
 268                default:
 269                        return -EINVAL;
 270        }
 271        return 0;
 272}
 273
 274/* reset device */
 275static int snd_opl3_reset_seq_oss(struct snd_seq_oss_arg *arg)
 276{
 277        struct snd_opl3 *opl3;
 278
 279        if (snd_BUG_ON(!arg))
 280                return -ENXIO;
 281        opl3 = arg->private_data;
 282
 283        return 0;
 284}
 285