GNU Linux-libre 4.14.290-gnu1
[releases.git] / sound / drivers / opl3 / opl3_oss.c
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 <linux/export.h>
22 #include "opl3_voice.h"
23
24 static int snd_opl3_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure);
25 static int snd_opl3_close_seq_oss(struct snd_seq_oss_arg *arg);
26 static int snd_opl3_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd, unsigned long ioarg);
27 static int snd_opl3_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format, const char __user *buf, int offs, int count);
28 static int snd_opl3_reset_seq_oss(struct snd_seq_oss_arg *arg);
29
30 /* operators */
31
32 extern struct snd_midi_op opl3_ops;
33
34 static struct snd_seq_oss_callback oss_callback = {
35         .owner =        THIS_MODULE,
36         .open =         snd_opl3_open_seq_oss,
37         .close =        snd_opl3_close_seq_oss,
38         .ioctl =        snd_opl3_ioctl_seq_oss,
39         .load_patch =   snd_opl3_load_patch_seq_oss,
40         .reset =        snd_opl3_reset_seq_oss,
41 };
42
43 static int snd_opl3_oss_event_input(struct snd_seq_event *ev, int direct,
44                                     void *private_data, int atomic, int hop)
45 {
46         struct snd_opl3 *opl3 = private_data;
47
48         if (ev->type != SNDRV_SEQ_EVENT_OSS)
49                 snd_midi_process_event(&opl3_ops, ev, opl3->oss_chset);
50         return 0;
51 }
52
53 /* ------------------------------ */
54
55 static void snd_opl3_oss_free_port(void *private_data)
56 {
57         struct snd_opl3 *opl3 = private_data;
58
59         snd_midi_channel_free_set(opl3->oss_chset);
60 }
61
62 static int snd_opl3_oss_create_port(struct snd_opl3 * opl3)
63 {
64         struct snd_seq_port_callback callbacks;
65         char name[32];
66         int voices, opl_ver;
67
68         voices = (opl3->hardware < OPL3_HW_OPL3) ?
69                 MAX_OPL2_VOICES : MAX_OPL3_VOICES;
70         opl3->oss_chset = snd_midi_channel_alloc_set(voices);
71         if (opl3->oss_chset == NULL)
72                 return -ENOMEM;
73         opl3->oss_chset->private_data = opl3;
74
75         memset(&callbacks, 0, sizeof(callbacks));
76         callbacks.owner = THIS_MODULE;
77         callbacks.event_input = snd_opl3_oss_event_input;
78         callbacks.private_free = snd_opl3_oss_free_port;
79         callbacks.private_data = opl3;
80
81         opl_ver = (opl3->hardware & OPL3_HW_MASK) >> 8;
82         sprintf(name, "OPL%i OSS Port", opl_ver);
83
84         opl3->oss_chset->client = opl3->seq_client;
85         opl3->oss_chset->port = snd_seq_event_port_attach(opl3->seq_client, &callbacks,
86                                                           SNDRV_SEQ_PORT_CAP_WRITE,
87                                                           SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
88                                                           SNDRV_SEQ_PORT_TYPE_MIDI_GM |
89                                                           SNDRV_SEQ_PORT_TYPE_HARDWARE |
90                                                           SNDRV_SEQ_PORT_TYPE_SYNTHESIZER,
91                                                           voices, voices,
92                                                           name);
93         if (opl3->oss_chset->port < 0) {
94                 int port;
95                 port = opl3->oss_chset->port;
96                 snd_midi_channel_free_set(opl3->oss_chset);
97                 return port;
98         }
99         return 0;
100 }
101
102 /* ------------------------------ */
103
104 /* register OSS synth */
105 void snd_opl3_init_seq_oss(struct snd_opl3 *opl3, char *name)
106 {
107         struct snd_seq_oss_reg *arg;
108         struct snd_seq_device *dev;
109
110         if (snd_seq_device_new(opl3->card, 0, SNDRV_SEQ_DEV_ID_OSS,
111                                sizeof(struct snd_seq_oss_reg), &dev) < 0)
112                 return;
113
114         opl3->oss_seq_dev = dev;
115         strlcpy(dev->name, name, sizeof(dev->name));
116         arg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
117         arg->type = SYNTH_TYPE_FM;
118         if (opl3->hardware < OPL3_HW_OPL3) {
119                 arg->subtype = FM_TYPE_ADLIB;
120                 arg->nvoices = MAX_OPL2_VOICES;
121         } else {
122                 arg->subtype = FM_TYPE_OPL3;
123                 arg->nvoices = MAX_OPL3_VOICES;
124         }
125         arg->oper = oss_callback;
126         arg->private_data = opl3;
127
128         if (snd_opl3_oss_create_port(opl3)) {
129                 /* register to OSS synth table */
130                 snd_device_register(opl3->card, dev);
131         }
132 }
133
134 /* unregister */
135 void snd_opl3_free_seq_oss(struct snd_opl3 *opl3)
136 {
137         if (opl3->oss_seq_dev) {
138                 /* The instance should have been released in prior */
139                 opl3->oss_seq_dev = NULL;
140         }
141 }
142
143 /* ------------------------------ */
144
145 /* open OSS sequencer */
146 static int snd_opl3_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure)
147 {
148         struct snd_opl3 *opl3 = closure;
149         int err;
150
151         if (snd_BUG_ON(!arg))
152                 return -ENXIO;
153
154         if ((err = snd_opl3_synth_setup(opl3)) < 0)
155                 return err;
156
157         /* fill the argument data */
158         arg->private_data = opl3;
159         arg->addr.client = opl3->oss_chset->client;
160         arg->addr.port = opl3->oss_chset->port;
161
162         if ((err = snd_opl3_synth_use_inc(opl3)) < 0)
163                 return err;
164
165         opl3->synth_mode = SNDRV_OPL3_MODE_SYNTH;
166         return 0;
167 }
168
169 /* close OSS sequencer */
170 static int snd_opl3_close_seq_oss(struct snd_seq_oss_arg *arg)
171 {
172         struct snd_opl3 *opl3;
173
174         if (snd_BUG_ON(!arg))
175                 return -ENXIO;
176         opl3 = arg->private_data;
177
178         snd_opl3_synth_cleanup(opl3);
179
180         snd_opl3_synth_use_dec(opl3);
181         return 0;
182 }
183
184 /* load patch */
185
186 /* from sound_config.h */
187 #define SBFM_MAXINSTR   256
188
189 static int snd_opl3_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format,
190                                        const char __user *buf, int offs, int count)
191 {
192         struct snd_opl3 *opl3;
193         struct sbi_instrument sbi;
194         char name[32];
195         int err, type;
196
197         if (snd_BUG_ON(!arg))
198                 return -ENXIO;
199         opl3 = arg->private_data;
200
201         if (format == FM_PATCH)
202                 type = FM_PATCH_OPL2;
203         else if (format == OPL3_PATCH)
204                 type = FM_PATCH_OPL3;
205         else
206                 return -EINVAL;
207
208         if (count < (int)sizeof(sbi)) {
209                 snd_printk(KERN_ERR "FM Error: Patch record too short\n");
210                 return -EINVAL;
211         }
212         if (copy_from_user(&sbi, buf, sizeof(sbi)))
213                 return -EFAULT;
214
215         if (sbi.channel < 0 || sbi.channel >= SBFM_MAXINSTR) {
216                 snd_printk(KERN_ERR "FM Error: Invalid instrument number %d\n",
217                            sbi.channel);
218                 return -EINVAL;
219         }
220
221         memset(name, 0, sizeof(name));
222         sprintf(name, "Chan%d", sbi.channel);
223
224         err = snd_opl3_load_patch(opl3, sbi.channel, 127, type, name, NULL,
225                                   sbi.operators);
226         if (err < 0)
227                 return err;
228
229         return sizeof(sbi);
230 }
231
232 /* ioctl */
233 static int snd_opl3_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd,
234                                   unsigned long ioarg)
235 {
236         struct snd_opl3 *opl3;
237
238         if (snd_BUG_ON(!arg))
239                 return -ENXIO;
240         opl3 = arg->private_data;
241         switch (cmd) {
242                 case SNDCTL_FM_LOAD_INSTR:
243                         snd_printk(KERN_ERR "OPL3: "
244                                    "Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. "
245                                    "Fix the program.\n");
246                         return -EINVAL;
247
248                 case SNDCTL_SYNTH_MEMAVL:
249                         return 0x7fffffff;
250
251                 case SNDCTL_FM_4OP_ENABLE:
252                         // handled automatically by OPL instrument type
253                         return 0;
254
255                 default:
256                         return -EINVAL;
257         }
258         return 0;
259 }
260
261 /* reset device */
262 static int snd_opl3_reset_seq_oss(struct snd_seq_oss_arg *arg)
263 {
264         struct snd_opl3 *opl3;
265
266         if (snd_BUG_ON(!arg))
267                 return -ENXIO;
268         opl3 = arg->private_data;
269
270         return 0;
271 }