Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/drivers/opl3/opl3_oss.c
10817 views
1
/*
2
* Interface for OSS sequencer emulation
3
*
4
* Copyright (C) 2000 Uros Bizjak <[email protected]>
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
23
static int snd_opl3_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure);
24
static int snd_opl3_close_seq_oss(struct snd_seq_oss_arg *arg);
25
static int snd_opl3_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd, unsigned long ioarg);
26
static int snd_opl3_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format, const char __user *buf, int offs, int count);
27
static int snd_opl3_reset_seq_oss(struct snd_seq_oss_arg *arg);
28
29
/* */
30
31
static 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
38
static inline void snd_leave_user(mm_segment_t fs)
39
{
40
set_fs(fs);
41
}
42
43
/* operators */
44
45
extern struct snd_midi_op opl3_ops;
46
47
static 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
56
static 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
68
static 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
75
static 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 */
118
void 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 */
148
void 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 */
159
static 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 */
183
static 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
202
static 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 */
246
static 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 */
275
static 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
286