]>
Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
c1017a4c | 2 | * Copyright (c) by Jaroslav Kysela <perex@perex.cz> |
1da177e4 LT |
3 | * Routines for control of SoundBlaster cards - MIDI interface |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | * | |
19 | * -- | |
20 | * | |
21 | * Sun May 9 22:54:38 BST 1999 George David Morrison <gdm@gedamo.demon.co.uk> | |
22 | * Fixed typo in snd_sb8dsp_midi_new_device which prevented midi from | |
23 | * working. | |
24 | * | |
25 | * Sun May 11 12:34:56 UTC 2003 Clemens Ladisch <clemens@ladisch.de> | |
26 | * Added full duplex UART mode for DSP version 2.0 and later. | |
27 | */ | |
28 | ||
29 | #include <sound/driver.h> | |
30 | #include <asm/io.h> | |
31 | #include <linux/time.h> | |
32 | #include <sound/core.h> | |
33 | #include <sound/sb.h> | |
34 | ||
1da177e4 | 35 | |
63eb1e4b | 36 | irqreturn_t snd_sb8dsp_midi_interrupt(struct snd_sb *chip) |
1da177e4 | 37 | { |
029d64b0 | 38 | struct snd_rawmidi *rmidi; |
1da177e4 LT |
39 | int max = 64; |
40 | char byte; | |
41 | ||
63eb1e4b JJ |
42 | if (!chip) |
43 | return IRQ_NONE; | |
44 | ||
45 | rmidi = chip->rmidi; | |
46 | if (!rmidi) { | |
1da177e4 LT |
47 | inb(SBP(chip, DATA_AVAIL)); /* ack interrupt */ |
48 | return IRQ_NONE; | |
49 | } | |
63eb1e4b | 50 | |
1da177e4 LT |
51 | spin_lock(&chip->midi_input_lock); |
52 | while (max-- > 0) { | |
53 | if (inb(SBP(chip, DATA_AVAIL)) & 0x80) { | |
54 | byte = inb(SBP(chip, READ)); | |
55 | if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) { | |
56 | snd_rawmidi_receive(chip->midi_substream_input, &byte, 1); | |
57 | } | |
58 | } | |
59 | } | |
60 | spin_unlock(&chip->midi_input_lock); | |
61 | return IRQ_HANDLED; | |
62 | } | |
63 | ||
029d64b0 | 64 | static int snd_sb8dsp_midi_input_open(struct snd_rawmidi_substream *substream) |
1da177e4 LT |
65 | { |
66 | unsigned long flags; | |
029d64b0 | 67 | struct snd_sb *chip; |
1da177e4 LT |
68 | unsigned int valid_open_flags; |
69 | ||
70 | chip = substream->rmidi->private_data; | |
71 | valid_open_flags = chip->hardware >= SB_HW_20 | |
72 | ? SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER : 0; | |
73 | spin_lock_irqsave(&chip->open_lock, flags); | |
74 | if (chip->open & ~valid_open_flags) { | |
75 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
76 | return -EAGAIN; | |
77 | } | |
78 | chip->open |= SB_OPEN_MIDI_INPUT; | |
79 | chip->midi_substream_input = substream; | |
80 | if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) { | |
81 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
82 | snd_sbdsp_reset(chip); /* reset DSP */ | |
83 | if (chip->hardware >= SB_HW_20) | |
84 | snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ); | |
85 | } else { | |
86 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
87 | } | |
88 | return 0; | |
89 | } | |
90 | ||
029d64b0 | 91 | static int snd_sb8dsp_midi_output_open(struct snd_rawmidi_substream *substream) |
1da177e4 LT |
92 | { |
93 | unsigned long flags; | |
029d64b0 | 94 | struct snd_sb *chip; |
1da177e4 LT |
95 | unsigned int valid_open_flags; |
96 | ||
97 | chip = substream->rmidi->private_data; | |
98 | valid_open_flags = chip->hardware >= SB_HW_20 | |
99 | ? SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER : 0; | |
100 | spin_lock_irqsave(&chip->open_lock, flags); | |
101 | if (chip->open & ~valid_open_flags) { | |
102 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
103 | return -EAGAIN; | |
104 | } | |
105 | chip->open |= SB_OPEN_MIDI_OUTPUT; | |
106 | chip->midi_substream_output = substream; | |
107 | if (!(chip->open & SB_OPEN_MIDI_INPUT)) { | |
108 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
109 | snd_sbdsp_reset(chip); /* reset DSP */ | |
110 | if (chip->hardware >= SB_HW_20) | |
111 | snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ); | |
112 | } else { | |
113 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
114 | } | |
115 | return 0; | |
116 | } | |
117 | ||
029d64b0 | 118 | static int snd_sb8dsp_midi_input_close(struct snd_rawmidi_substream *substream) |
1da177e4 LT |
119 | { |
120 | unsigned long flags; | |
029d64b0 | 121 | struct snd_sb *chip; |
1da177e4 LT |
122 | |
123 | chip = substream->rmidi->private_data; | |
124 | spin_lock_irqsave(&chip->open_lock, flags); | |
125 | chip->open &= ~(SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER); | |
126 | chip->midi_substream_input = NULL; | |
127 | if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) { | |
128 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
129 | snd_sbdsp_reset(chip); /* reset DSP */ | |
130 | } else { | |
131 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
132 | } | |
133 | return 0; | |
134 | } | |
135 | ||
029d64b0 | 136 | static int snd_sb8dsp_midi_output_close(struct snd_rawmidi_substream *substream) |
1da177e4 LT |
137 | { |
138 | unsigned long flags; | |
029d64b0 | 139 | struct snd_sb *chip; |
1da177e4 LT |
140 | |
141 | chip = substream->rmidi->private_data; | |
142 | spin_lock_irqsave(&chip->open_lock, flags); | |
143 | chip->open &= ~(SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER); | |
144 | chip->midi_substream_output = NULL; | |
145 | if (!(chip->open & SB_OPEN_MIDI_INPUT)) { | |
146 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
147 | snd_sbdsp_reset(chip); /* reset DSP */ | |
148 | } else { | |
149 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
150 | } | |
151 | return 0; | |
152 | } | |
153 | ||
029d64b0 | 154 | static void snd_sb8dsp_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) |
1da177e4 LT |
155 | { |
156 | unsigned long flags; | |
029d64b0 | 157 | struct snd_sb *chip; |
1da177e4 LT |
158 | |
159 | chip = substream->rmidi->private_data; | |
160 | spin_lock_irqsave(&chip->open_lock, flags); | |
161 | if (up) { | |
162 | if (!(chip->open & SB_OPEN_MIDI_INPUT_TRIGGER)) { | |
163 | if (chip->hardware < SB_HW_20) | |
164 | snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ); | |
165 | chip->open |= SB_OPEN_MIDI_INPUT_TRIGGER; | |
166 | } | |
167 | } else { | |
168 | if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) { | |
169 | if (chip->hardware < SB_HW_20) | |
170 | snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ); | |
171 | chip->open &= ~SB_OPEN_MIDI_INPUT_TRIGGER; | |
172 | } | |
173 | } | |
174 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
175 | } | |
176 | ||
029d64b0 | 177 | static void snd_sb8dsp_midi_output_write(struct snd_rawmidi_substream *substream) |
1da177e4 LT |
178 | { |
179 | unsigned long flags; | |
029d64b0 | 180 | struct snd_sb *chip; |
1da177e4 LT |
181 | char byte; |
182 | int max = 32; | |
183 | ||
184 | /* how big is Tx FIFO? */ | |
185 | chip = substream->rmidi->private_data; | |
186 | while (max-- > 0) { | |
187 | spin_lock_irqsave(&chip->open_lock, flags); | |
188 | if (snd_rawmidi_transmit_peek(substream, &byte, 1) != 1) { | |
189 | chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER; | |
190 | del_timer(&chip->midi_timer); | |
191 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
192 | break; | |
193 | } | |
194 | if (chip->hardware >= SB_HW_20) { | |
195 | int timeout = 8; | |
196 | while ((inb(SBP(chip, STATUS)) & 0x80) != 0 && --timeout > 0) | |
197 | ; | |
198 | if (timeout == 0) { | |
199 | /* Tx FIFO full - try again later */ | |
200 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
201 | break; | |
202 | } | |
203 | outb(byte, SBP(chip, WRITE)); | |
204 | } else { | |
205 | snd_sbdsp_command(chip, SB_DSP_MIDI_OUTPUT); | |
206 | snd_sbdsp_command(chip, byte); | |
207 | } | |
208 | snd_rawmidi_transmit_ack(substream, 1); | |
209 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
210 | } | |
211 | } | |
212 | ||
213 | static void snd_sb8dsp_midi_output_timer(unsigned long data) | |
214 | { | |
029d64b0 TI |
215 | struct snd_rawmidi_substream *substream = (struct snd_rawmidi_substream *) data; |
216 | struct snd_sb * chip = substream->rmidi->private_data; | |
1da177e4 LT |
217 | unsigned long flags; |
218 | ||
219 | spin_lock_irqsave(&chip->open_lock, flags); | |
220 | chip->midi_timer.expires = 1 + jiffies; | |
221 | add_timer(&chip->midi_timer); | |
222 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
223 | snd_sb8dsp_midi_output_write(substream); | |
224 | } | |
225 | ||
029d64b0 | 226 | static void snd_sb8dsp_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) |
1da177e4 LT |
227 | { |
228 | unsigned long flags; | |
029d64b0 | 229 | struct snd_sb *chip; |
1da177e4 LT |
230 | |
231 | chip = substream->rmidi->private_data; | |
232 | spin_lock_irqsave(&chip->open_lock, flags); | |
233 | if (up) { | |
234 | if (!(chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER)) { | |
235 | init_timer(&chip->midi_timer); | |
236 | chip->midi_timer.function = snd_sb8dsp_midi_output_timer; | |
237 | chip->midi_timer.data = (unsigned long) substream; | |
238 | chip->midi_timer.expires = 1 + jiffies; | |
239 | add_timer(&chip->midi_timer); | |
240 | chip->open |= SB_OPEN_MIDI_OUTPUT_TRIGGER; | |
241 | } | |
242 | } else { | |
243 | if (chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER) { | |
244 | chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER; | |
245 | } | |
246 | } | |
247 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
248 | ||
249 | if (up) | |
250 | snd_sb8dsp_midi_output_write(substream); | |
251 | } | |
252 | ||
029d64b0 | 253 | static struct snd_rawmidi_ops snd_sb8dsp_midi_output = |
1da177e4 LT |
254 | { |
255 | .open = snd_sb8dsp_midi_output_open, | |
256 | .close = snd_sb8dsp_midi_output_close, | |
257 | .trigger = snd_sb8dsp_midi_output_trigger, | |
258 | }; | |
259 | ||
029d64b0 | 260 | static struct snd_rawmidi_ops snd_sb8dsp_midi_input = |
1da177e4 LT |
261 | { |
262 | .open = snd_sb8dsp_midi_input_open, | |
263 | .close = snd_sb8dsp_midi_input_close, | |
264 | .trigger = snd_sb8dsp_midi_input_trigger, | |
265 | }; | |
266 | ||
029d64b0 | 267 | int snd_sb8dsp_midi(struct snd_sb *chip, int device, struct snd_rawmidi ** rrawmidi) |
1da177e4 | 268 | { |
029d64b0 | 269 | struct snd_rawmidi *rmidi; |
1da177e4 LT |
270 | int err; |
271 | ||
272 | if (rrawmidi) | |
273 | *rrawmidi = NULL; | |
274 | if ((err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi)) < 0) | |
275 | return err; | |
276 | strcpy(rmidi->name, "SB8 MIDI"); | |
277 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_sb8dsp_midi_output); | |
278 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_sb8dsp_midi_input); | |
279 | rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT; | |
280 | if (chip->hardware >= SB_HW_20) | |
281 | rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; | |
282 | rmidi->private_data = chip; | |
283 | chip->rmidi = rmidi; | |
284 | if (rrawmidi) | |
285 | *rrawmidi = rmidi; | |
286 | return 0; | |
287 | } |