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