11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Interface for OSS sequencer emulation 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (C) 2000 Uros Bizjak <uros@kss-loka.si> 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 71da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 81da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 91da177e4SLinus Torvalds * (at your option) any later version. 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 121da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 131da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 141da177e4SLinus Torvalds * GNU General Public License for more details. 151da177e4SLinus Torvalds * 161da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 171da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 181da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 191da177e4SLinus Torvalds */ 201da177e4SLinus Torvalds 21*d81a6d71SPaul Gortmaker #include <linux/export.h> 221da177e4SLinus Torvalds #include "opl3_voice.h" 231da177e4SLinus Torvalds 245b1646a8STakashi Iwai static int snd_opl3_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure); 255b1646a8STakashi Iwai static int snd_opl3_close_seq_oss(struct snd_seq_oss_arg *arg); 265b1646a8STakashi Iwai static int snd_opl3_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd, unsigned long ioarg); 275b1646a8STakashi Iwai static int snd_opl3_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format, const char __user *buf, int offs, int count); 285b1646a8STakashi Iwai static int snd_opl3_reset_seq_oss(struct snd_seq_oss_arg *arg); 291da177e4SLinus Torvalds 301da177e4SLinus Torvalds /* */ 311da177e4SLinus Torvalds 321da177e4SLinus Torvalds static inline mm_segment_t snd_enter_user(void) 331da177e4SLinus Torvalds { 341da177e4SLinus Torvalds mm_segment_t fs = get_fs(); 351da177e4SLinus Torvalds set_fs(get_ds()); 361da177e4SLinus Torvalds return fs; 371da177e4SLinus Torvalds } 381da177e4SLinus Torvalds 391da177e4SLinus Torvalds static inline void snd_leave_user(mm_segment_t fs) 401da177e4SLinus Torvalds { 411da177e4SLinus Torvalds set_fs(fs); 421da177e4SLinus Torvalds } 431da177e4SLinus Torvalds 441da177e4SLinus Torvalds /* operators */ 451da177e4SLinus Torvalds 465b1646a8STakashi Iwai extern struct snd_midi_op opl3_ops; 471da177e4SLinus Torvalds 485b1646a8STakashi Iwai static struct snd_seq_oss_callback oss_callback = { 491da177e4SLinus Torvalds .owner = THIS_MODULE, 501da177e4SLinus Torvalds .open = snd_opl3_open_seq_oss, 511da177e4SLinus Torvalds .close = snd_opl3_close_seq_oss, 521da177e4SLinus Torvalds .ioctl = snd_opl3_ioctl_seq_oss, 531da177e4SLinus Torvalds .load_patch = snd_opl3_load_patch_seq_oss, 541da177e4SLinus Torvalds .reset = snd_opl3_reset_seq_oss, 551da177e4SLinus Torvalds }; 561da177e4SLinus Torvalds 575b1646a8STakashi Iwai static int snd_opl3_oss_event_input(struct snd_seq_event *ev, int direct, 581da177e4SLinus Torvalds void *private_data, int atomic, int hop) 591da177e4SLinus Torvalds { 605b1646a8STakashi Iwai struct snd_opl3 *opl3 = private_data; 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds if (ev->type != SNDRV_SEQ_EVENT_OSS) 631da177e4SLinus Torvalds snd_midi_process_event(&opl3_ops, ev, opl3->oss_chset); 641da177e4SLinus Torvalds return 0; 651da177e4SLinus Torvalds } 661da177e4SLinus Torvalds 671da177e4SLinus Torvalds /* ------------------------------ */ 681da177e4SLinus Torvalds 691da177e4SLinus Torvalds static void snd_opl3_oss_free_port(void *private_data) 701da177e4SLinus Torvalds { 715b1646a8STakashi Iwai struct snd_opl3 *opl3 = private_data; 721da177e4SLinus Torvalds 731da177e4SLinus Torvalds snd_midi_channel_free_set(opl3->oss_chset); 741da177e4SLinus Torvalds } 751da177e4SLinus Torvalds 765b1646a8STakashi Iwai static int snd_opl3_oss_create_port(struct snd_opl3 * opl3) 771da177e4SLinus Torvalds { 785b1646a8STakashi Iwai struct snd_seq_port_callback callbacks; 791da177e4SLinus Torvalds char name[32]; 801da177e4SLinus Torvalds int voices, opl_ver; 811da177e4SLinus Torvalds 821da177e4SLinus Torvalds voices = (opl3->hardware < OPL3_HW_OPL3) ? 831da177e4SLinus Torvalds MAX_OPL2_VOICES : MAX_OPL3_VOICES; 841da177e4SLinus Torvalds opl3->oss_chset = snd_midi_channel_alloc_set(voices); 851da177e4SLinus Torvalds if (opl3->oss_chset == NULL) 861da177e4SLinus Torvalds return -ENOMEM; 871da177e4SLinus Torvalds opl3->oss_chset->private_data = opl3; 881da177e4SLinus Torvalds 891da177e4SLinus Torvalds memset(&callbacks, 0, sizeof(callbacks)); 901da177e4SLinus Torvalds callbacks.owner = THIS_MODULE; 911da177e4SLinus Torvalds callbacks.event_input = snd_opl3_oss_event_input; 921da177e4SLinus Torvalds callbacks.private_free = snd_opl3_oss_free_port; 931da177e4SLinus Torvalds callbacks.private_data = opl3; 941da177e4SLinus Torvalds 951da177e4SLinus Torvalds opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8; 961da177e4SLinus Torvalds sprintf(name, "OPL%i OSS Port", opl_ver); 971da177e4SLinus Torvalds 981da177e4SLinus Torvalds opl3->oss_chset->client = opl3->seq_client; 991da177e4SLinus Torvalds opl3->oss_chset->port = snd_seq_event_port_attach(opl3->seq_client, &callbacks, 1001da177e4SLinus Torvalds SNDRV_SEQ_PORT_CAP_WRITE, 1011da177e4SLinus Torvalds SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | 102450047a7SClemens Ladisch SNDRV_SEQ_PORT_TYPE_MIDI_GM | 103450047a7SClemens Ladisch SNDRV_SEQ_PORT_TYPE_HARDWARE | 104450047a7SClemens Ladisch SNDRV_SEQ_PORT_TYPE_SYNTHESIZER, 1051da177e4SLinus Torvalds voices, voices, 1061da177e4SLinus Torvalds name); 1071da177e4SLinus Torvalds if (opl3->oss_chset->port < 0) { 1085e315e92SDave Jones int port; 1095e315e92SDave Jones port = opl3->oss_chset->port; 1101da177e4SLinus Torvalds snd_midi_channel_free_set(opl3->oss_chset); 1115e315e92SDave Jones return port; 1121da177e4SLinus Torvalds } 1131da177e4SLinus Torvalds return 0; 1141da177e4SLinus Torvalds } 1151da177e4SLinus Torvalds 1161da177e4SLinus Torvalds /* ------------------------------ */ 1171da177e4SLinus Torvalds 1181da177e4SLinus Torvalds /* register OSS synth */ 1195b1646a8STakashi Iwai void snd_opl3_init_seq_oss(struct snd_opl3 *opl3, char *name) 1201da177e4SLinus Torvalds { 1215b1646a8STakashi Iwai struct snd_seq_oss_reg *arg; 1225b1646a8STakashi Iwai struct snd_seq_device *dev; 1231da177e4SLinus Torvalds 1241da177e4SLinus Torvalds if (snd_seq_device_new(opl3->card, 0, SNDRV_SEQ_DEV_ID_OSS, 1255b1646a8STakashi Iwai sizeof(struct snd_seq_oss_reg), &dev) < 0) 1261da177e4SLinus Torvalds return; 1271da177e4SLinus Torvalds 1281da177e4SLinus Torvalds opl3->oss_seq_dev = dev; 1291da177e4SLinus Torvalds strlcpy(dev->name, name, sizeof(dev->name)); 1301da177e4SLinus Torvalds arg = SNDRV_SEQ_DEVICE_ARGPTR(dev); 1311da177e4SLinus Torvalds arg->type = SYNTH_TYPE_FM; 1321da177e4SLinus Torvalds if (opl3->hardware < OPL3_HW_OPL3) { 1331da177e4SLinus Torvalds arg->subtype = FM_TYPE_ADLIB; 1341da177e4SLinus Torvalds arg->nvoices = MAX_OPL2_VOICES; 1351da177e4SLinus Torvalds } else { 1361da177e4SLinus Torvalds arg->subtype = FM_TYPE_OPL3; 1371da177e4SLinus Torvalds arg->nvoices = MAX_OPL3_VOICES; 1381da177e4SLinus Torvalds } 1391da177e4SLinus Torvalds arg->oper = oss_callback; 1401da177e4SLinus Torvalds arg->private_data = opl3; 1411da177e4SLinus Torvalds 1425e315e92SDave Jones if (snd_opl3_oss_create_port(opl3)) { 1431da177e4SLinus Torvalds /* register to OSS synth table */ 1441da177e4SLinus Torvalds snd_device_register(opl3->card, dev); 1451da177e4SLinus Torvalds } 1465e315e92SDave Jones } 1471da177e4SLinus Torvalds 1481da177e4SLinus Torvalds /* unregister */ 1495b1646a8STakashi Iwai void snd_opl3_free_seq_oss(struct snd_opl3 *opl3) 1501da177e4SLinus Torvalds { 1511da177e4SLinus Torvalds if (opl3->oss_seq_dev) { 152b1a3aa20STakashi Iwai /* The instance should have been released in prior */ 1531da177e4SLinus Torvalds opl3->oss_seq_dev = NULL; 1541da177e4SLinus Torvalds } 1551da177e4SLinus Torvalds } 1561da177e4SLinus Torvalds 1571da177e4SLinus Torvalds /* ------------------------------ */ 1581da177e4SLinus Torvalds 1591da177e4SLinus Torvalds /* open OSS sequencer */ 1605b1646a8STakashi Iwai static int snd_opl3_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure) 1611da177e4SLinus Torvalds { 1625b1646a8STakashi Iwai struct snd_opl3 *opl3 = closure; 1631da177e4SLinus Torvalds int err; 1641da177e4SLinus Torvalds 1655e246b85STakashi Iwai if (snd_BUG_ON(!arg)) 1665e246b85STakashi Iwai return -ENXIO; 1671da177e4SLinus Torvalds 1681da177e4SLinus Torvalds if ((err = snd_opl3_synth_setup(opl3)) < 0) 1691da177e4SLinus Torvalds return err; 1701da177e4SLinus Torvalds 1711da177e4SLinus Torvalds /* fill the argument data */ 1721da177e4SLinus Torvalds arg->private_data = opl3; 1731da177e4SLinus Torvalds arg->addr.client = opl3->oss_chset->client; 1741da177e4SLinus Torvalds arg->addr.port = opl3->oss_chset->port; 1751da177e4SLinus Torvalds 1761da177e4SLinus Torvalds if ((err = snd_opl3_synth_use_inc(opl3)) < 0) 1771da177e4SLinus Torvalds return err; 1781da177e4SLinus Torvalds 1791da177e4SLinus Torvalds opl3->synth_mode = SNDRV_OPL3_MODE_SYNTH; 1801da177e4SLinus Torvalds return 0; 1811da177e4SLinus Torvalds } 1821da177e4SLinus Torvalds 1831da177e4SLinus Torvalds /* close OSS sequencer */ 1845b1646a8STakashi Iwai static int snd_opl3_close_seq_oss(struct snd_seq_oss_arg *arg) 1851da177e4SLinus Torvalds { 1865b1646a8STakashi Iwai struct snd_opl3 *opl3; 1871da177e4SLinus Torvalds 1885e246b85STakashi Iwai if (snd_BUG_ON(!arg)) 1895e246b85STakashi Iwai return -ENXIO; 1901da177e4SLinus Torvalds opl3 = arg->private_data; 1911da177e4SLinus Torvalds 1921da177e4SLinus Torvalds snd_opl3_synth_cleanup(opl3); 1931da177e4SLinus Torvalds 1941da177e4SLinus Torvalds snd_opl3_synth_use_dec(opl3); 1951da177e4SLinus Torvalds return 0; 1961da177e4SLinus Torvalds } 1971da177e4SLinus Torvalds 1981da177e4SLinus Torvalds /* load patch */ 1991da177e4SLinus Torvalds 2001da177e4SLinus Torvalds /* from sound_config.h */ 2011da177e4SLinus Torvalds #define SBFM_MAXINSTR 256 2021da177e4SLinus Torvalds 2035b1646a8STakashi Iwai static int snd_opl3_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format, 2041da177e4SLinus Torvalds const char __user *buf, int offs, int count) 2051da177e4SLinus Torvalds { 2065b1646a8STakashi Iwai struct snd_opl3 *opl3; 207224a0332STakashi Iwai struct sbi_instrument sbi; 208224a0332STakashi Iwai char name[32]; 209224a0332STakashi Iwai int err, type; 2101da177e4SLinus Torvalds 2115e246b85STakashi Iwai if (snd_BUG_ON(!arg)) 2125e246b85STakashi Iwai return -ENXIO; 2131da177e4SLinus Torvalds opl3 = arg->private_data; 2141da177e4SLinus Torvalds 215224a0332STakashi Iwai if (format == FM_PATCH) 216224a0332STakashi Iwai type = FM_PATCH_OPL2; 217224a0332STakashi Iwai else if (format == OPL3_PATCH) 218224a0332STakashi Iwai type = FM_PATCH_OPL3; 219224a0332STakashi Iwai else 220224a0332STakashi Iwai return -EINVAL; 2211da177e4SLinus Torvalds 2221da177e4SLinus Torvalds if (count < (int)sizeof(sbi)) { 22345203832STakashi Iwai snd_printk(KERN_ERR "FM Error: Patch record too short\n"); 2241da177e4SLinus Torvalds return -EINVAL; 2251da177e4SLinus Torvalds } 2261da177e4SLinus Torvalds if (copy_from_user(&sbi, buf, sizeof(sbi))) 2271da177e4SLinus Torvalds return -EFAULT; 2281da177e4SLinus Torvalds 2291da177e4SLinus Torvalds if (sbi.channel < 0 || sbi.channel >= SBFM_MAXINSTR) { 23045203832STakashi Iwai snd_printk(KERN_ERR "FM Error: Invalid instrument number %d\n", 231224a0332STakashi Iwai sbi.channel); 2321da177e4SLinus Torvalds return -EINVAL; 2331da177e4SLinus Torvalds } 2341da177e4SLinus Torvalds 235224a0332STakashi Iwai memset(name, 0, sizeof(name)); 236224a0332STakashi Iwai sprintf(name, "Chan%d", sbi.channel); 2371da177e4SLinus Torvalds 238224a0332STakashi Iwai err = snd_opl3_load_patch(opl3, sbi.channel, 127, type, name, NULL, 239224a0332STakashi Iwai sbi.operators); 240224a0332STakashi Iwai if (err < 0) 2411da177e4SLinus Torvalds return err; 242224a0332STakashi Iwai 243224a0332STakashi Iwai return sizeof(sbi); 2441da177e4SLinus Torvalds } 2451da177e4SLinus Torvalds 2461da177e4SLinus Torvalds /* ioctl */ 2475b1646a8STakashi Iwai static int snd_opl3_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd, 2481da177e4SLinus Torvalds unsigned long ioarg) 2491da177e4SLinus Torvalds { 2505b1646a8STakashi Iwai struct snd_opl3 *opl3; 2511da177e4SLinus Torvalds 2525e246b85STakashi Iwai if (snd_BUG_ON(!arg)) 2535e246b85STakashi Iwai return -ENXIO; 2541da177e4SLinus Torvalds opl3 = arg->private_data; 2551da177e4SLinus Torvalds switch (cmd) { 2561da177e4SLinus Torvalds case SNDCTL_FM_LOAD_INSTR: 25745203832STakashi Iwai snd_printk(KERN_ERR "OPL3: " 25845203832STakashi Iwai "Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. " 25945203832STakashi Iwai "Fix the program.\n"); 2601da177e4SLinus Torvalds return -EINVAL; 2611da177e4SLinus Torvalds 2621da177e4SLinus Torvalds case SNDCTL_SYNTH_MEMAVL: 2631da177e4SLinus Torvalds return 0x7fffffff; 2641da177e4SLinus Torvalds 2651da177e4SLinus Torvalds case SNDCTL_FM_4OP_ENABLE: 2661da177e4SLinus Torvalds // handled automatically by OPL instrument type 2671da177e4SLinus Torvalds return 0; 2681da177e4SLinus Torvalds 2691da177e4SLinus Torvalds default: 2701da177e4SLinus Torvalds return -EINVAL; 2711da177e4SLinus Torvalds } 2721da177e4SLinus Torvalds return 0; 2731da177e4SLinus Torvalds } 2741da177e4SLinus Torvalds 2751da177e4SLinus Torvalds /* reset device */ 2765b1646a8STakashi Iwai static int snd_opl3_reset_seq_oss(struct snd_seq_oss_arg *arg) 2771da177e4SLinus Torvalds { 2785b1646a8STakashi Iwai struct snd_opl3 *opl3; 2791da177e4SLinus Torvalds 2805e246b85STakashi Iwai if (snd_BUG_ON(!arg)) 2815e246b85STakashi Iwai return -ENXIO; 2821da177e4SLinus Torvalds opl3 = arg->private_data; 2831da177e4SLinus Torvalds 2841da177e4SLinus Torvalds return 0; 2851da177e4SLinus Torvalds } 286