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