Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz> | |
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 | ||
35 | /* | |
36 | ||
37 | */ | |
38 | ||
029d64b0 | 39 | irqreturn_t snd_sb8dsp_midi_interrupt(struct snd_sb * chip) |
1da177e4 | 40 | { |
029d64b0 | 41 | struct snd_rawmidi *rmidi; |
1da177e4 LT |
42 | int max = 64; |
43 | char byte; | |
44 | ||
45 | if (chip == NULL || (rmidi = chip->rmidi) == NULL) { | |
46 | inb(SBP(chip, DATA_AVAIL)); /* ack interrupt */ | |
47 | return IRQ_NONE; | |
48 | } | |
49 | spin_lock(&chip->midi_input_lock); | |
50 | while (max-- > 0) { | |
51 | if (inb(SBP(chip, DATA_AVAIL)) & 0x80) { | |
52 | byte = inb(SBP(chip, READ)); | |
53 | if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) { | |
54 | snd_rawmidi_receive(chip->midi_substream_input, &byte, 1); | |
55 | } | |
56 | } | |
57 | } | |
58 | spin_unlock(&chip->midi_input_lock); | |
59 | return IRQ_HANDLED; | |
60 | } | |
61 | ||
62 | /* | |
63 | ||
64 | */ | |
65 | ||
029d64b0 | 66 | static int snd_sb8dsp_midi_input_open(struct snd_rawmidi_substream *substream) |
1da177e4 LT |
67 | { |
68 | unsigned long flags; | |
029d64b0 | 69 | struct snd_sb *chip; |
1da177e4 LT |
70 | unsigned int valid_open_flags; |
71 | ||
72 | chip = substream->rmidi->private_data; | |
73 | valid_open_flags = chip->hardware >= SB_HW_20 | |
74 | ? SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER : 0; | |
75 | spin_lock_irqsave(&chip->open_lock, flags); | |
76 | if (chip->open & ~valid_open_flags) { | |
77 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
78 | return -EAGAIN; | |
79 | } | |
80 | chip->open |= SB_OPEN_MIDI_INPUT; | |
81 | chip->midi_substream_input = substream; | |
82 | if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) { | |
83 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
84 | snd_sbdsp_reset(chip); /* reset DSP */ | |
85 | if (chip->hardware >= SB_HW_20) | |
86 | snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ); | |
87 | } else { | |
88 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
89 | } | |
90 | return 0; | |
91 | } | |
92 | ||
029d64b0 | 93 | static int snd_sb8dsp_midi_output_open(struct snd_rawmidi_substream *substream) |
1da177e4 LT |
94 | { |
95 | unsigned long flags; | |
029d64b0 | 96 | struct snd_sb *chip; |
1da177e4 LT |
97 | unsigned int valid_open_flags; |
98 | ||
99 | chip = substream->rmidi->private_data; | |
100 | valid_open_flags = chip->hardware >= SB_HW_20 | |
101 | ? SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER : 0; | |
102 | spin_lock_irqsave(&chip->open_lock, flags); | |
103 | if (chip->open & ~valid_open_flags) { | |
104 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
105 | return -EAGAIN; | |
106 | } | |
107 | chip->open |= SB_OPEN_MIDI_OUTPUT; | |
108 | chip->midi_substream_output = substream; | |
109 | if (!(chip->open & SB_OPEN_MIDI_INPUT)) { | |
110 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
111 | snd_sbdsp_reset(chip); /* reset DSP */ | |
112 | if (chip->hardware >= SB_HW_20) | |
113 | snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ); | |
114 | } else { | |
115 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
116 | } | |
117 | return 0; | |
118 | } | |
119 | ||
029d64b0 | 120 | static int snd_sb8dsp_midi_input_close(struct snd_rawmidi_substream *substream) |
1da177e4 LT |
121 | { |
122 | unsigned long flags; | |
029d64b0 | 123 | struct snd_sb *chip; |
1da177e4 LT |
124 | |
125 | chip = substream->rmidi->private_data; | |
126 | spin_lock_irqsave(&chip->open_lock, flags); | |
127 | chip->open &= ~(SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER); | |
128 | chip->midi_substream_input = NULL; | |
129 | if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) { | |
130 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
131 | snd_sbdsp_reset(chip); /* reset DSP */ | |
132 | } else { | |
133 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
134 | } | |
135 | return 0; | |
136 | } | |
137 | ||
029d64b0 | 138 | static int snd_sb8dsp_midi_output_close(struct snd_rawmidi_substream *substream) |
1da177e4 LT |
139 | { |
140 | unsigned long flags; | |
029d64b0 | 141 | struct snd_sb *chip; |
1da177e4 LT |
142 | |
143 | chip = substream->rmidi->private_data; | |
144 | spin_lock_irqsave(&chip->open_lock, flags); | |
145 | chip->open &= ~(SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER); | |
146 | chip->midi_substream_output = NULL; | |
147 | if (!(chip->open & SB_OPEN_MIDI_INPUT)) { | |
148 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
149 | snd_sbdsp_reset(chip); /* reset DSP */ | |
150 | } else { | |
151 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
152 | } | |
153 | return 0; | |
154 | } | |
155 | ||
029d64b0 | 156 | static void snd_sb8dsp_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) |
1da177e4 LT |
157 | { |
158 | unsigned long flags; | |
029d64b0 | 159 | struct snd_sb *chip; |
1da177e4 LT |
160 | |
161 | chip = substream->rmidi->private_data; | |
162 | spin_lock_irqsave(&chip->open_lock, flags); | |
163 | if (up) { | |
164 | if (!(chip->open & SB_OPEN_MIDI_INPUT_TRIGGER)) { | |
165 | if (chip->hardware < SB_HW_20) | |
166 | snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ); | |
167 | chip->open |= SB_OPEN_MIDI_INPUT_TRIGGER; | |
168 | } | |
169 | } else { | |
170 | if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) { | |
171 | if (chip->hardware < SB_HW_20) | |
172 | snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ); | |
173 | chip->open &= ~SB_OPEN_MIDI_INPUT_TRIGGER; | |
174 | } | |
175 | } | |
176 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
177 | } | |
178 | ||
029d64b0 | 179 | static void snd_sb8dsp_midi_output_write(struct snd_rawmidi_substream *substream) |
1da177e4 LT |
180 | { |
181 | unsigned long flags; | |
029d64b0 | 182 | struct snd_sb *chip; |
1da177e4 LT |
183 | char byte; |
184 | int max = 32; | |
185 | ||
186 | /* how big is Tx FIFO? */ | |
187 | chip = substream->rmidi->private_data; | |
188 | while (max-- > 0) { | |
189 | spin_lock_irqsave(&chip->open_lock, flags); | |
190 | if (snd_rawmidi_transmit_peek(substream, &byte, 1) != 1) { | |
191 | chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER; | |
192 | del_timer(&chip->midi_timer); | |
193 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
194 | break; | |
195 | } | |
196 | if (chip->hardware >= SB_HW_20) { | |
197 | int timeout = 8; | |
198 | while ((inb(SBP(chip, STATUS)) & 0x80) != 0 && --timeout > 0) | |
199 | ; | |
200 | if (timeout == 0) { | |
201 | /* Tx FIFO full - try again later */ | |
202 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
203 | break; | |
204 | } | |
205 | outb(byte, SBP(chip, WRITE)); | |
206 | } else { | |
207 | snd_sbdsp_command(chip, SB_DSP_MIDI_OUTPUT); | |
208 | snd_sbdsp_command(chip, byte); | |
209 | } | |
210 | snd_rawmidi_transmit_ack(substream, 1); | |
211 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
212 | } | |
213 | } | |
214 | ||
215 | static void snd_sb8dsp_midi_output_timer(unsigned long data) | |
216 | { | |
029d64b0 TI |
217 | struct snd_rawmidi_substream *substream = (struct snd_rawmidi_substream *) data; |
218 | struct snd_sb * chip = substream->rmidi->private_data; | |
1da177e4 LT |
219 | unsigned long flags; |
220 | ||
221 | spin_lock_irqsave(&chip->open_lock, flags); | |
222 | chip->midi_timer.expires = 1 + jiffies; | |
223 | add_timer(&chip->midi_timer); | |
224 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
225 | snd_sb8dsp_midi_output_write(substream); | |
226 | } | |
227 | ||
029d64b0 | 228 | static void snd_sb8dsp_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) |
1da177e4 LT |
229 | { |
230 | unsigned long flags; | |
029d64b0 | 231 | struct snd_sb *chip; |
1da177e4 LT |
232 | |
233 | chip = substream->rmidi->private_data; | |
234 | spin_lock_irqsave(&chip->open_lock, flags); | |
235 | if (up) { | |
236 | if (!(chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER)) { | |
237 | init_timer(&chip->midi_timer); | |
238 | chip->midi_timer.function = snd_sb8dsp_midi_output_timer; | |
239 | chip->midi_timer.data = (unsigned long) substream; | |
240 | chip->midi_timer.expires = 1 + jiffies; | |
241 | add_timer(&chip->midi_timer); | |
242 | chip->open |= SB_OPEN_MIDI_OUTPUT_TRIGGER; | |
243 | } | |
244 | } else { | |
245 | if (chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER) { | |
246 | chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER; | |
247 | } | |
248 | } | |
249 | spin_unlock_irqrestore(&chip->open_lock, flags); | |
250 | ||
251 | if (up) | |
252 | snd_sb8dsp_midi_output_write(substream); | |
253 | } | |
254 | ||
255 | /* | |
256 | ||
257 | */ | |
258 | ||
029d64b0 | 259 | static struct snd_rawmidi_ops snd_sb8dsp_midi_output = |
1da177e4 LT |
260 | { |
261 | .open = snd_sb8dsp_midi_output_open, | |
262 | .close = snd_sb8dsp_midi_output_close, | |
263 | .trigger = snd_sb8dsp_midi_output_trigger, | |
264 | }; | |
265 | ||
029d64b0 | 266 | static struct snd_rawmidi_ops snd_sb8dsp_midi_input = |
1da177e4 LT |
267 | { |
268 | .open = snd_sb8dsp_midi_input_open, | |
269 | .close = snd_sb8dsp_midi_input_close, | |
270 | .trigger = snd_sb8dsp_midi_input_trigger, | |
271 | }; | |
272 | ||
029d64b0 | 273 | int snd_sb8dsp_midi(struct snd_sb *chip, int device, struct snd_rawmidi ** rrawmidi) |
1da177e4 | 274 | { |
029d64b0 | 275 | struct snd_rawmidi *rmidi; |
1da177e4 LT |
276 | int err; |
277 | ||
278 | if (rrawmidi) | |
279 | *rrawmidi = NULL; | |
280 | if ((err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi)) < 0) | |
281 | return err; | |
282 | strcpy(rmidi->name, "SB8 MIDI"); | |
283 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_sb8dsp_midi_output); | |
284 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_sb8dsp_midi_input); | |
285 | rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT; | |
286 | if (chip->hardware >= SB_HW_20) | |
287 | rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; | |
288 | rmidi->private_data = chip; | |
289 | chip->rmidi = rmidi; | |
290 | if (rrawmidi) | |
291 | *rrawmidi = rmidi; | |
292 | return 0; | |
293 | } |