1 | /*
|
---|
2 | * serial.c
|
---|
3 | * Copyright (c) by Jaroslav Kysela <perex@suse.cz>,
|
---|
4 | * Isaku Yamahata <yamahata@private.email.ne.jp>,
|
---|
5 | * George Hansper <ghansper@apana.org.au>,
|
---|
6 | * Hannu Savolainen
|
---|
7 | *
|
---|
8 | * This code is based on the code from ALSA 0.5.9, but heavily rewritten.
|
---|
9 | *
|
---|
10 | * Sat Mar 31 17:27:57 PST 2001 tim.mann@compaq.com
|
---|
11 | * Added support for the Midiator MS-124T and for the MS-124W in
|
---|
12 | * Single Addressed (S/A) or Multiple Burst (M/B) mode, with
|
---|
13 | * power derived either parasitically from the serial port or
|
---|
14 | * from a separate power supply.
|
---|
15 | *
|
---|
16 | * The new snd_adaptor module parameter allows you to select
|
---|
17 | * either the default Roland Soundcanvas support (0), which was
|
---|
18 | * previously included in this driver but was not documented,
|
---|
19 | * Midiator MS-124T support (1), Midiator MS-124W S/A mode
|
---|
20 | * support (2), or MS-124W M/B mode support (3). For the
|
---|
21 | * Midiator MS-124W, you must set the physical M-S and A-B
|
---|
22 | * switches on the Midiator to match the driver mode you select.
|
---|
23 | *
|
---|
24 | * - In Roland Soundcanvas mode, multiple ALSA raw MIDI
|
---|
25 | * substreams are supported (midiCnD0-midiCnD15). Whenever you
|
---|
26 | * write to a different substream, the driver sends the
|
---|
27 | * nonstandard MIDI command sequence F5 NN, where NN is the
|
---|
28 | * substream number plus 1. Roland modules use this command to
|
---|
29 | * switch between different "parts", so this feature lets you
|
---|
30 | * treat each part as a distinct raw MIDI substream. The driver
|
---|
31 | * provides no way to send F5 00 (no selection) or to not send
|
---|
32 | * the F5 NN command sequence at all; perhaps it ought to.
|
---|
33 | *
|
---|
34 | * - In MS-124T mode, one raw MIDI substream is supported
|
---|
35 | * (midiCnD0); the snd_outs module parameter is automatically set
|
---|
36 | * to 1. The driver sends the same data to all four MIDI Out
|
---|
37 | * connectors. Set the A-B switch and the snd_speed module
|
---|
38 | * parameter to match (A=19200, B=9600).
|
---|
39 | *
|
---|
40 | * Usage example for MS-124T, with A-B switch in A position:
|
---|
41 | * setserial /dev/ttyS0 uart none
|
---|
42 | * /sbin/modprobe snd-card-serial snd_port=0x3f8 snd_irq=4 \
|
---|
43 | * snd_adaptor=1 snd_speed=19200
|
---|
44 | *
|
---|
45 | * - In MS-124W S/A mode, one raw MIDI substream is supported
|
---|
46 | * (midiCnD0); the snd_outs module parameter is automatically set
|
---|
47 | * to 1. The driver sends the same data to all four MIDI Out
|
---|
48 | * connectors at full MIDI speed.
|
---|
49 | *
|
---|
50 | * Usage example for S/A mode:
|
---|
51 | * setserial /dev/ttyS0 uart none
|
---|
52 | * /sbin/modprobe snd-card-serial snd_port=0x3f8 snd_irq=4 \
|
---|
53 | * snd_adaptor=2
|
---|
54 | *
|
---|
55 | * - In MS-124W M/B mode, the driver supports 16 ALSA raw MIDI
|
---|
56 | * substreams; the snd_outs module parameter is automatically set
|
---|
57 | * to 16. The substream number gives a bitmask of which MIDI Out
|
---|
58 | * connectors the data should be sent to, with midiCnD1 sending
|
---|
59 | * to Out 1, midiCnD2 to Out 2, midiCnD4 to Out 3, and midiCnD8
|
---|
60 | * to Out 4. Thus midiCnD15 sends the data to all 4 ports. As a
|
---|
61 | * special case, midiCnD0 also sends to all ports, since it is
|
---|
62 | * not useful to send the data to no ports. M/B mode has extra
|
---|
63 | * overhead to select the MIDI Out for each byte, so the
|
---|
64 | * aggregate data rate across all four MIDI Outs is at most one
|
---|
65 | * byte every 520 us, as compared with the full MIDI data rate of
|
---|
66 | * one byte every 320 us per port.
|
---|
67 | *
|
---|
68 | * Usage example for M/B mode:
|
---|
69 | * setserial /dev/ttyS0 uart none
|
---|
70 | * /sbin/insmod snd-card-serial snd_port=0x3f8 snd_irq=4 \
|
---|
71 | * snd_adaptor=3
|
---|
72 | *
|
---|
73 | * - The MS-124W hardware's M/A mode is currently not supported.
|
---|
74 | * This mode allows the MIDI Outs to act independently at double
|
---|
75 | * the aggregate throughput of M/B, but does not allow sending
|
---|
76 | * the same byte simultaneously to multiple MIDI Outs. The M/A
|
---|
77 | * protocol requires the driver to twiddle the modem control
|
---|
78 | * lines under timing constraints, so it would be a bit more
|
---|
79 | * complicated to implement than the other modes.
|
---|
80 | *
|
---|
81 | * - Midiator models other than MS-124W and MS-124T are currently
|
---|
82 | * not supported. Note that the suffix letter is significant;
|
---|
83 | * the MS-124 and MS-124B are not compatible, nor are the other
|
---|
84 | * known models MS-101, MS-101B, MS-103, and MS-114. I do have
|
---|
85 | * documentation that partially covers these models, but no units
|
---|
86 | * to experiment with. The MS-124W support is tested with a real
|
---|
87 | * unit. The MS-124T support is untested, but should work.
|
---|
88 | *
|
---|
89 | * This program is free software; you can redistribute it and/or modify
|
---|
90 | * it under the terms of the GNU General Public License as published by
|
---|
91 | * the Free Software Foundation; either version 2 of the License, or
|
---|
92 | * (at your option) any later version.
|
---|
93 | *
|
---|
94 | * This program is distributed in the hope that it will be useful,
|
---|
95 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
96 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
97 | * GNU General Public License for more details.
|
---|
98 | *
|
---|
99 | * You should have received a copy of the GNU General Public License
|
---|
100 | * along with this program; if not, write to the Free Software
|
---|
101 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
---|
102 | *
|
---|
103 | */
|
---|
104 |
|
---|
105 |
|
---|
106 | #define SNDRV_MAIN_OBJECT_FILE
|
---|
107 |
|
---|
108 | #include <sound/driver.h>
|
---|
109 | #include <sound/rawmidi.h>
|
---|
110 | #define SNDRV_GET_ID
|
---|
111 | #include <sound/initval.h>
|
---|
112 |
|
---|
113 | #include <linux/serial_reg.h>
|
---|
114 |
|
---|
115 | EXPORT_NO_SYMBOLS;
|
---|
116 | MODULE_DESCRIPTION("MIDI serial");
|
---|
117 | MODULE_CLASSES("{sound}");
|
---|
118 | MODULE_DEVICES("{{ALSA, MIDI serial}}");
|
---|
119 |
|
---|
120 | #define SNDRV_SERIAL_SOUNDCANVAS 0 /* Roland Soundcanvas; F5 NN selects part */
|
---|
121 | #define SNDRV_SERIAL_MS124T 1 /* Midiator MS-124T */
|
---|
122 | #define SNDRV_SERIAL_MS124W_SA 2 /* Midiator MS-124W in S/A mode */
|
---|
123 | #define SNDRV_SERIAL_MS124W_MB 3 /* Midiator MS-124W in M/B mode */
|
---|
124 | #define SNDRV_SERIAL_MAX_ADAPTOR SNDRV_SERIAL_MS124W_MB
|
---|
125 | static char *adaptor_names[] = {
|
---|
126 | "Soundcanvas",
|
---|
127 | "MS-124T",
|
---|
128 | "MS-124W S/A",
|
---|
129 | "MS-124W M/B"
|
---|
130 | };
|
---|
131 |
|
---|
132 | static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
|
---|
133 | static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
|
---|
134 | static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
|
---|
135 | static long snd_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x3f8,0x2f8,0x3e8,0x2e8 */
|
---|
136 | static int snd_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 3,4,5,7,9,10,11,14,15 */
|
---|
137 | #ifdef TARGET_OS2
|
---|
138 | static int snd_speed[SNDRV_CARDS] = {REPEAT_SNDRV(38400)}; /* 9600,19200,38400,57600,115200 */
|
---|
139 | static int snd_base[SNDRV_CARDS] = {REPEAT_SNDRV(115200)}; /* baud base */
|
---|
140 | static int snd_outs[SNDRV_CARDS] = {REPEAT_SNDRV(1)}; /* 1 to 16 */
|
---|
141 | static int snd_adaptor[SNDRV_CARDS] = {REPEAT_SNDRV(SNDRV_SERIAL_SOUNDCANVAS)};
|
---|
142 | #else
|
---|
143 | static int snd_speed[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 38400}; /* 9600,19200,38400,57600,115200 */
|
---|
144 | static int snd_base[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 115200}; /* baud base */
|
---|
145 | static int snd_outs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; /* 1 to 16 */
|
---|
146 | static int snd_adaptor[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = SNDRV_SERIAL_SOUNDCANVAS};
|
---|
147 | #endif
|
---|
148 |
|
---|
149 | MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
|
---|
150 | MODULE_PARM_DESC(snd_index, "Index value for Serial MIDI.");
|
---|
151 | MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC);
|
---|
152 | MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
|
---|
153 | MODULE_PARM_DESC(snd_id, "ID string for Serial MIDI.");
|
---|
154 | MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
|
---|
155 | MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
|
---|
156 | MODULE_PARM_DESC(snd_enable, "Enable UART16550A chip.");
|
---|
157 | MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
|
---|
158 | MODULE_PARM(snd_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
|
---|
159 | MODULE_PARM_DESC(snd_port, "Port # for UART16550A chip.");
|
---|
160 | MODULE_PARM_SYNTAX(snd_port, SNDRV_PORT12_DESC);
|
---|
161 | MODULE_PARM(snd_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
|
---|
162 | MODULE_PARM_DESC(snd_irq, "IRQ # for UART16550A chip.");
|
---|
163 | MODULE_PARM_SYNTAX(snd_irq, SNDRV_IRQ_DESC);
|
---|
164 | MODULE_PARM(snd_speed, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
|
---|
165 | MODULE_PARM_DESC(snd_speed, "Speed in bauds.");
|
---|
166 | MODULE_PARM_SYNTAX(snd_speed, SNDRV_ENABLED ",allows:{9600,19200,38400,57600,115200},dialog:list");
|
---|
167 | MODULE_PARM(snd_base, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
|
---|
168 | MODULE_PARM_DESC(snd_base, "Base for divisor in bauds.");
|
---|
169 | MODULE_PARM_SYNTAX(snd_base, SNDRV_ENABLED ",allows:{57600,115200,230400,460800},dialog:list");
|
---|
170 | MODULE_PARM(snd_outs, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
|
---|
171 | MODULE_PARM_DESC(snd_outs, "Number of MIDI outputs.");
|
---|
172 | MODULE_PARM_SYNTAX(snd_outs, SNDRV_ENABLED ",allows:{{1,16}},dialog:list");
|
---|
173 | MODULE_PARM(snd_adaptor, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
|
---|
174 | MODULE_PARM_DESC(snd_adaptor, "Type of adaptor.");
|
---|
175 | MODULE_PARM_SYNTAX(snd_adaptor, SNDRV_ENABLED ",allows:{{0=Soundcanvas,1=MS-124T,2=MS-124W S/A,3=MS-124W M/B}},dialog:list");
|
---|
176 |
|
---|
177 | /*#define SNDRV_SERIAL_MS124W_MB_NOCOMBO 1*/ /* Address outs as 0-3 instead of bitmap */
|
---|
178 |
|
---|
179 | #define SNDRV_SERIAL_MAX_OUTS 16 /* max 64, min 16 */
|
---|
180 |
|
---|
181 | #define TX_BUFF_SIZE (1<<9) /* Must be 2^n */
|
---|
182 | #define TX_BUFF_MASK (TX_BUFF_SIZE - 1)
|
---|
183 |
|
---|
184 | #define SERIAL_MODE_NOT_OPENED (0)
|
---|
185 | #define SERIAL_MODE_INPUT_OPEN (1 << 0)
|
---|
186 | #define SERIAL_MODE_OUTPUT_OPEN (1 << 1)
|
---|
187 | #define SERIAL_MODE_INPUT_TRIGGERED (1 << 2)
|
---|
188 | #define SERIAL_MODE_OUTPUT_TRIGGERED (1 << 3)
|
---|
189 |
|
---|
190 | typedef struct _snd_uart16550 {
|
---|
191 | snd_card_t *card;
|
---|
192 | snd_rawmidi_t *rmidi;
|
---|
193 | snd_rawmidi_substream_t *midi_output[SNDRV_SERIAL_MAX_OUTS];
|
---|
194 | snd_rawmidi_substream_t *midi_input;
|
---|
195 |
|
---|
196 | int filemode; //open status of file
|
---|
197 |
|
---|
198 | spinlock_t open_lock;
|
---|
199 |
|
---|
200 | int irq;
|
---|
201 |
|
---|
202 | unsigned long base;
|
---|
203 | struct resource *res_base;
|
---|
204 |
|
---|
205 | unsigned int speed;
|
---|
206 | unsigned int speed_base;
|
---|
207 | unsigned char divisor;
|
---|
208 |
|
---|
209 | unsigned char old_divisor_lsb;
|
---|
210 | unsigned char old_divisor_msb;
|
---|
211 | unsigned char old_line_ctrl_reg;
|
---|
212 |
|
---|
213 | // parameter for using of write loop
|
---|
214 | short int fifo_limit; //used in uart16550
|
---|
215 | short int fifo_count; //used in uart16550
|
---|
216 |
|
---|
217 | // type of adaptor
|
---|
218 | int adaptor;
|
---|
219 |
|
---|
220 | // outputs
|
---|
221 | int prev_out;
|
---|
222 | unsigned char prev_status[SNDRV_SERIAL_MAX_OUTS];
|
---|
223 |
|
---|
224 | // write buffer and its writing/reading position
|
---|
225 | unsigned char tx_buff[TX_BUFF_SIZE];
|
---|
226 | int buff_in_count;
|
---|
227 | int buff_in;
|
---|
228 | int buff_out;
|
---|
229 |
|
---|
230 | // wait timer
|
---|
231 | unsigned int timer_running:1;
|
---|
232 | struct timer_list buffer_timer;
|
---|
233 |
|
---|
234 | } snd_uart16550_t;
|
---|
235 |
|
---|
236 | static snd_card_t *snd_serial_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
|
---|
237 |
|
---|
238 | inline static void snd_uart16550_add_timer(snd_uart16550_t *uart)
|
---|
239 | {
|
---|
240 | if (! uart->timer_running) {
|
---|
241 | /* timer 38600bps * 10bit * 16byte */
|
---|
242 | uart->buffer_timer.expires = jiffies + (HZ+255)/256;
|
---|
243 | uart->timer_running = 1;
|
---|
244 | add_timer(&uart->buffer_timer);
|
---|
245 | }
|
---|
246 | }
|
---|
247 |
|
---|
248 | inline static void snd_uart16550_del_timer(snd_uart16550_t *uart)
|
---|
249 | {
|
---|
250 | if (uart->timer_running) {
|
---|
251 | del_timer(&uart->buffer_timer);
|
---|
252 | uart->timer_running = 0;
|
---|
253 | }
|
---|
254 | }
|
---|
255 |
|
---|
256 | /* This macro is only used in snd_uart16550_io_loop */
|
---|
257 | inline static void snd_uart16550_buffer_output(snd_uart16550_t *uart)
|
---|
258 | {
|
---|
259 | unsigned short buff_out = uart->buff_out;
|
---|
260 | outb(uart->tx_buff[buff_out], uart->base + UART_TX);
|
---|
261 | uart->fifo_count++;
|
---|
262 | buff_out++;
|
---|
263 | buff_out &= TX_BUFF_MASK;
|
---|
264 | uart->buff_out = buff_out;
|
---|
265 | uart->buff_in_count--;
|
---|
266 | }
|
---|
267 |
|
---|
268 | /* This loop should be called with interrupts disabled
|
---|
269 | * We don't want to interrupt this,
|
---|
270 | * as we're already handling an interupt
|
---|
271 | */
|
---|
272 | static void snd_uart16550_io_loop(snd_uart16550_t * uart)
|
---|
273 | {
|
---|
274 | unsigned char c, status;
|
---|
275 |
|
---|
276 | /* Read Loop */
|
---|
277 | while ((status = inb(uart->base + UART_LSR)) & UART_LSR_DR) {
|
---|
278 | /* while receive data ready */
|
---|
279 | c = inb(uart->base + UART_RX);
|
---|
280 | if (uart->filemode & SERIAL_MODE_INPUT_OPEN) {
|
---|
281 | snd_rawmidi_receive(uart->midi_input, &c, 1);
|
---|
282 | }
|
---|
283 | if (status & UART_LSR_OE)
|
---|
284 | snd_printk("%s: Overrun on device at 0x%lx\n",
|
---|
285 | uart->rmidi->name, uart->base);
|
---|
286 | }
|
---|
287 |
|
---|
288 | /* no need of check SERIAL_MODE_OUTPUT_OPEN because if not,
|
---|
289 | buffer is never filled. */
|
---|
290 | /* Check write status */
|
---|
291 | if (status & UART_LSR_THRE) {
|
---|
292 | uart->fifo_count = 0;
|
---|
293 | }
|
---|
294 | if (uart->adaptor == SNDRV_SERIAL_MS124W_SA) {
|
---|
295 | /* Can't use FIFO, must send only when CTS is true */
|
---|
296 | status = inb(uart->base + UART_MSR);
|
---|
297 | if (uart->fifo_count == 0 && (status & UART_MSR_CTS)
|
---|
298 | && uart->buff_in_count > 0)
|
---|
299 | snd_uart16550_buffer_output(uart);
|
---|
300 | } else {
|
---|
301 | /* Write loop */
|
---|
302 | while (uart->fifo_count < uart->fifo_limit /* Can we write ? */
|
---|
303 | && uart->buff_in_count > 0) /* Do we want to? */
|
---|
304 | snd_uart16550_buffer_output(uart);
|
---|
305 | }
|
---|
306 | if (uart->irq < 0 && uart->buff_in_count > 0)
|
---|
307 | snd_uart16550_add_timer(uart);
|
---|
308 | }
|
---|
309 |
|
---|
310 | /* NOTES ON SERVICING INTERUPTS
|
---|
311 | * ---------------------------
|
---|
312 | * After receiving a interrupt, it is important to indicate to the UART that
|
---|
313 | * this has been done.
|
---|
314 | * For a Rx interupt, this is done by reading the received byte.
|
---|
315 | * For a Tx interupt this is done by either:
|
---|
316 | * a) Writing a byte
|
---|
317 | * b) Reading the IIR
|
---|
318 | * It is particularly important to read the IIR if a Tx interupt is received
|
---|
319 | * when there is no data in tx_buff[], as in this case there no other
|
---|
320 | * indication that the interupt has been serviced, and it remains outstanding
|
---|
321 | * indefinitely. This has the curious side effect that and no further interupts
|
---|
322 | * will be generated from this device AT ALL!!.
|
---|
323 | * It is also desirable to clear outstanding interupts when the device is
|
---|
324 | * opened/closed.
|
---|
325 | *
|
---|
326 | *
|
---|
327 | * Note that some devices need OUT2 to be set before they will generate
|
---|
328 | * interrupts at all. (Possibly tied to an internal pull-up on CTS?)
|
---|
329 | */
|
---|
330 | static void snd_uart16550_interrupt(int irq, void *dev_id, struct pt_regs *regs)
|
---|
331 | {
|
---|
332 | snd_uart16550_t *uart;
|
---|
333 |
|
---|
334 | uart = (snd_uart16550_t *) dev_id;
|
---|
335 | spin_lock(&uart->open_lock);
|
---|
336 | if (uart->filemode == SERIAL_MODE_NOT_OPENED) {
|
---|
337 | spin_unlock(&uart->open_lock);
|
---|
338 | return;
|
---|
339 | }
|
---|
340 | inb(uart->base + UART_IIR); /* indicate to the UART that the interupt has been serviced */
|
---|
341 | snd_uart16550_io_loop(uart);
|
---|
342 | spin_unlock(&uart->open_lock);
|
---|
343 | }
|
---|
344 |
|
---|
345 | /* When the polling mode, this function calls snd_uart16550_io_loop. */
|
---|
346 | static void snd_uart16550_buffer_timer(unsigned long data)
|
---|
347 | {
|
---|
348 | snd_uart16550_t *uart;
|
---|
349 |
|
---|
350 | uart = (snd_uart16550_t *)data;
|
---|
351 | spin_lock(&uart->open_lock);
|
---|
352 | snd_uart16550_del_timer(uart);
|
---|
353 | snd_uart16550_io_loop(uart);
|
---|
354 | spin_unlock(&uart->open_lock);
|
---|
355 | }
|
---|
356 |
|
---|
357 | /*
|
---|
358 | * this method probes, if an uart sits on given port
|
---|
359 | * return 0 if found
|
---|
360 | * return negative error if not found
|
---|
361 | */
|
---|
362 | static int __init snd_uart16550_detect(unsigned int io_base)
|
---|
363 | {
|
---|
364 | int ok;
|
---|
365 | unsigned char c;
|
---|
366 |
|
---|
367 | if (check_region(io_base, 8))
|
---|
368 | return -EBUSY;
|
---|
369 |
|
---|
370 | /* Do some vague tests for the presence of the uart */
|
---|
371 | if (io_base == 0)
|
---|
372 | return -ENODEV; /* Not configured */
|
---|
373 |
|
---|
374 | ok = 1; /* uart detected unless one of the following tests should fail */
|
---|
375 | /* 8 data-bits, 1 stop-bit, parity off, DLAB = 0 */
|
---|
376 | outb(UART_LCR_WLEN8, io_base + UART_LCR); /* Line Control Register */
|
---|
377 | c = inb(io_base + UART_IER);
|
---|
378 | /* The top four bits of the IER should always == 0 */
|
---|
379 | if ((c & 0xf0) != 0)
|
---|
380 | ok = 0; /* failed */
|
---|
381 |
|
---|
382 | outb(0xaa, io_base + UART_SCR);
|
---|
383 | /* Write arbitrary data into the scratch reg */
|
---|
384 | c = inb(io_base + UART_SCR);
|
---|
385 | /* If it comes back, it's OK */
|
---|
386 | if (c != 0xaa)
|
---|
387 | ok = 0; /* failed */
|
---|
388 |
|
---|
389 | outb(0x55, io_base + UART_SCR);
|
---|
390 | /* Write arbitrary data into the scratch reg */
|
---|
391 | c = inb(io_base + UART_SCR);
|
---|
392 | /* If it comes back, it's OK */
|
---|
393 | if (c != 0x55)
|
---|
394 | ok = 0; /* failed */
|
---|
395 |
|
---|
396 | return ok;
|
---|
397 | }
|
---|
398 |
|
---|
399 | static void snd_uart16550_do_open(snd_uart16550_t * uart)
|
---|
400 | {
|
---|
401 | char byte;
|
---|
402 |
|
---|
403 | /* Initialize basic variables */
|
---|
404 | uart->buff_in_count = 0;
|
---|
405 | uart->buff_in = 0;
|
---|
406 | uart->buff_out = 0;
|
---|
407 | uart->fifo_limit = 1;
|
---|
408 | uart->fifo_count = 0;
|
---|
409 | uart->timer_running = 0;
|
---|
410 |
|
---|
411 | outb(UART_FCR_ENABLE_FIFO /* Enable FIFO's (if available) */
|
---|
412 | | UART_FCR_CLEAR_RCVR /* Clear receiver FIFO */
|
---|
413 | | UART_FCR_CLEAR_XMIT /* Clear transmitter FIFO */
|
---|
414 | | UART_FCR_TRIGGER_4 /* Set FIFO trigger at 4-bytes */
|
---|
415 | /* NOTE: interupt generated after T=(time)4-bytes
|
---|
416 | * if less than UART_FCR_TRIGGER bytes received
|
---|
417 | */
|
---|
418 | ,uart->base + UART_FCR); /* FIFO Control Register */
|
---|
419 |
|
---|
420 | if ((inb(uart->base + UART_IIR) & 0xf0) == 0xc0)
|
---|
421 | uart->fifo_limit = 16;
|
---|
422 | if (uart->divisor != 0) {
|
---|
423 | uart->old_line_ctrl_reg = inb(uart->base + UART_LCR);
|
---|
424 | outb(UART_LCR_DLAB /* Divisor latch access bit */
|
---|
425 | ,uart->base + UART_LCR); /* Line Control Register */
|
---|
426 | uart->old_divisor_lsb = inb(uart->base + UART_DLL);
|
---|
427 | uart->old_divisor_msb = inb(uart->base + UART_DLM);
|
---|
428 |
|
---|
429 | outb(uart->divisor
|
---|
430 | ,uart->base + UART_DLL); /* Divisor Latch Low */
|
---|
431 | outb(0
|
---|
432 | ,uart->base + UART_DLM); /* Divisor Latch High */
|
---|
433 | /* DLAB is reset to 0 in next outb() */
|
---|
434 | }
|
---|
435 | /* Set serial parameters (parity off, etc) */
|
---|
436 | outb(UART_LCR_WLEN8 /* 8 data-bits */
|
---|
437 | | 0 /* 1 stop-bit */
|
---|
438 | | 0 /* parity off */
|
---|
439 | | 0 /* DLAB = 0 */
|
---|
440 | ,uart->base + UART_LCR); /* Line Control Register */
|
---|
441 |
|
---|
442 | switch (uart->adaptor) {
|
---|
443 | default:
|
---|
444 | outb(UART_MCR_RTS /* Set Request-To-Send line active */
|
---|
445 | | UART_MCR_DTR /* Set Data-Terminal-Ready line active */
|
---|
446 | | UART_MCR_OUT2 /* Set OUT2 - not always required, but when
|
---|
447 | * it is, it is ESSENTIAL for enabling interrupts
|
---|
448 | */
|
---|
449 | ,uart->base + UART_MCR); /* Modem Control Register */
|
---|
450 | break;
|
---|
451 | case SNDRV_SERIAL_MS124W_SA:
|
---|
452 | case SNDRV_SERIAL_MS124W_MB:
|
---|
453 | /* MS-124W can draw power from RTS and DTR if they
|
---|
454 | are in opposite states. */
|
---|
455 | outb(UART_MCR_RTS | (0&UART_MCR_DTR) | UART_MCR_OUT2,
|
---|
456 | uart->base + UART_MCR);
|
---|
457 | break;
|
---|
458 | case SNDRV_SERIAL_MS124T:
|
---|
459 | /* MS-124T can draw power from RTS and/or DTR (preferably
|
---|
460 | both) if they are both asserted. */
|
---|
461 | outb(UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2,
|
---|
462 | uart->base + UART_MCR);
|
---|
463 | break;
|
---|
464 | }
|
---|
465 |
|
---|
466 | if (uart->irq < 0) {
|
---|
467 | byte = (0 & UART_IER_RDI) /* Disable Receiver data interupt */
|
---|
468 | |(0 & UART_IER_THRI) /* Disable Transmitter holding register empty interupt */
|
---|
469 | ;
|
---|
470 | } else if (uart->adaptor == SNDRV_SERIAL_MS124W_SA) {
|
---|
471 | byte = UART_IER_RDI /* Enable Receiver data interrupt */
|
---|
472 | | UART_IER_MSI /* Enable Modem status interrupt */
|
---|
473 | ;
|
---|
474 | } else {
|
---|
475 | byte = UART_IER_RDI /* Enable Receiver data interupt */
|
---|
476 | | UART_IER_THRI /* Enable Transmitter holding register empty interupt */
|
---|
477 | ;
|
---|
478 | }
|
---|
479 | outb(byte, uart->base + UART_IER); /* Interupt enable Register */
|
---|
480 |
|
---|
481 | inb(uart->base + UART_LSR); /* Clear any pre-existing overrun indication */
|
---|
482 | inb(uart->base + UART_IIR); /* Clear any pre-existing transmit interrupt */
|
---|
483 | inb(uart->base + UART_RX); /* Clear any pre-existing receive interrupt */
|
---|
484 | }
|
---|
485 |
|
---|
486 | static void snd_uart16550_do_close(snd_uart16550_t * uart)
|
---|
487 | {
|
---|
488 | if (uart->irq < 0)
|
---|
489 | snd_uart16550_del_timer(uart);
|
---|
490 |
|
---|
491 | /* NOTE: may need to disable interrupts before de-registering out handler.
|
---|
492 | * For now, the consequences are harmless.
|
---|
493 | */
|
---|
494 |
|
---|
495 | outb((0 & UART_IER_RDI) /* Disable Receiver data interupt */
|
---|
496 | |(0 & UART_IER_THRI) /* Disable Transmitter holding register empty interupt */
|
---|
497 | ,uart->base + UART_IER); /* Interupt enable Register */
|
---|
498 |
|
---|
499 | switch (uart->adaptor) {
|
---|
500 | default:
|
---|
501 | outb((0 & UART_MCR_RTS) /* Deactivate Request-To-Send line */
|
---|
502 | |(0 & UART_MCR_DTR) /* Deactivate Data-Terminal-Ready line */
|
---|
503 | |(0 & UART_MCR_OUT2) /* Deactivate OUT2 */
|
---|
504 | ,uart->base + UART_MCR); /* Modem Control Register */
|
---|
505 | break;
|
---|
506 | case SNDRV_SERIAL_MS124W_SA:
|
---|
507 | case SNDRV_SERIAL_MS124W_MB:
|
---|
508 | /* MS-124W can draw power from RTS and DTR if they
|
---|
509 | are in opposite states; leave it powered. */
|
---|
510 | outb(UART_MCR_RTS | (0&UART_MCR_DTR) | (0&UART_MCR_OUT2),
|
---|
511 | uart->base + UART_MCR);
|
---|
512 | break;
|
---|
513 | case SNDRV_SERIAL_MS124T:
|
---|
514 | /* MS-124T can draw power from RTS and/or DTR (preferably
|
---|
515 | both) if they are both asserted; leave it powered. */
|
---|
516 | outb(UART_MCR_RTS | UART_MCR_DTR | (0&UART_MCR_OUT2),
|
---|
517 | uart->base + UART_MCR);
|
---|
518 | break;
|
---|
519 | }
|
---|
520 |
|
---|
521 | inb(uart->base + UART_IIR); /* Clear any outstanding interupts */
|
---|
522 |
|
---|
523 | /* Restore old divisor */
|
---|
524 | if (uart->divisor != 0) {
|
---|
525 | outb(UART_LCR_DLAB /* Divisor latch access bit */
|
---|
526 | ,uart->base + UART_LCR); /* Line Control Register */
|
---|
527 | outb(uart->old_divisor_lsb
|
---|
528 | ,uart->base + UART_DLL); /* Divisor Latch Low */
|
---|
529 | outb(uart->old_divisor_msb
|
---|
530 | ,uart->base + UART_DLM); /* Divisor Latch High */
|
---|
531 | /* Restore old LCR (data bits, stop bits, parity, DLAB) */
|
---|
532 | outb(uart->old_line_ctrl_reg
|
---|
533 | ,uart->base + UART_LCR); /* Line Control Register */
|
---|
534 | }
|
---|
535 | }
|
---|
536 |
|
---|
537 | static int snd_uart16550_input_open(snd_rawmidi_substream_t * substream)
|
---|
538 | {
|
---|
539 | unsigned long flags;
|
---|
540 | snd_uart16550_t *uart = substream->rmidi->private_data;
|
---|
541 |
|
---|
542 | spin_lock_irqsave(&uart->open_lock, flags);
|
---|
543 | if (uart->filemode == SERIAL_MODE_NOT_OPENED)
|
---|
544 | snd_uart16550_do_open(uart);
|
---|
545 | uart->filemode |= SERIAL_MODE_INPUT_OPEN;
|
---|
546 | uart->midi_input = substream;
|
---|
547 | spin_unlock_irqrestore(&uart->open_lock, flags);
|
---|
548 | return 0;
|
---|
549 | }
|
---|
550 |
|
---|
551 | static int snd_uart16550_input_close(snd_rawmidi_substream_t * substream)
|
---|
552 | {
|
---|
553 | unsigned long flags;
|
---|
554 | snd_uart16550_t *uart = substream->rmidi->private_data;
|
---|
555 |
|
---|
556 | spin_lock_irqsave(&uart->open_lock, flags);
|
---|
557 | uart->filemode &= ~SERIAL_MODE_INPUT_OPEN;
|
---|
558 | uart->midi_input = NULL;
|
---|
559 | if (uart->filemode == SERIAL_MODE_NOT_OPENED)
|
---|
560 | snd_uart16550_do_close(uart);
|
---|
561 | spin_unlock_irqrestore(&uart->open_lock, flags);
|
---|
562 | return 0;
|
---|
563 | }
|
---|
564 |
|
---|
565 | static void snd_uart16550_input_trigger(snd_rawmidi_substream_t * substream, int up)
|
---|
566 | {
|
---|
567 | unsigned long flags;
|
---|
568 | snd_uart16550_t *uart = substream->rmidi->private_data;
|
---|
569 |
|
---|
570 | spin_lock_irqsave(&uart->open_lock, flags);
|
---|
571 | if (up) {
|
---|
572 | uart->filemode |= SERIAL_MODE_INPUT_TRIGGERED;
|
---|
573 | } else {
|
---|
574 | uart->filemode &= ~SERIAL_MODE_INPUT_TRIGGERED;
|
---|
575 | }
|
---|
576 | spin_unlock_irqrestore(&uart->open_lock, flags);
|
---|
577 | }
|
---|
578 |
|
---|
579 | static int snd_uart16550_output_open(snd_rawmidi_substream_t * substream)
|
---|
580 | {
|
---|
581 | unsigned long flags;
|
---|
582 | snd_uart16550_t *uart = substream->rmidi->private_data;
|
---|
583 |
|
---|
584 | spin_lock_irqsave(&uart->open_lock, flags);
|
---|
585 | if (uart->filemode == SERIAL_MODE_NOT_OPENED)
|
---|
586 | snd_uart16550_do_open(uart);
|
---|
587 | uart->filemode |= SERIAL_MODE_OUTPUT_OPEN;
|
---|
588 | uart->midi_output[substream->number] = substream;
|
---|
589 | spin_unlock_irqrestore(&uart->open_lock, flags);
|
---|
590 | return 0;
|
---|
591 | };
|
---|
592 |
|
---|
593 | static int snd_uart16550_output_close(snd_rawmidi_substream_t * substream)
|
---|
594 | {
|
---|
595 | unsigned long flags;
|
---|
596 | snd_uart16550_t *uart = substream->rmidi->private_data;
|
---|
597 |
|
---|
598 | spin_lock_irqsave(&uart->open_lock, flags);
|
---|
599 | uart->filemode &= ~SERIAL_MODE_OUTPUT_OPEN;
|
---|
600 | uart->midi_output[substream->number] = NULL;
|
---|
601 | if (uart->filemode == SERIAL_MODE_NOT_OPENED)
|
---|
602 | snd_uart16550_do_close(uart);
|
---|
603 | spin_unlock_irqrestore(&uart->open_lock, flags);
|
---|
604 | return 0;
|
---|
605 | };
|
---|
606 |
|
---|
607 | inline static void snd_uart16550_write_buffer(snd_uart16550_t *uart, unsigned char byte)
|
---|
608 | {
|
---|
609 | unsigned short buff_in = uart->buff_in;
|
---|
610 | uart->tx_buff[buff_in] = byte;
|
---|
611 | buff_in++;
|
---|
612 | buff_in &= TX_BUFF_MASK;
|
---|
613 | uart->buff_in = buff_in;
|
---|
614 | uart->buff_in_count++;
|
---|
615 | if (uart->irq < 0) /* polling mode */
|
---|
616 | snd_uart16550_add_timer(uart);
|
---|
617 | }
|
---|
618 |
|
---|
619 | static void snd_uart16550_output_byte(snd_uart16550_t *uart, snd_rawmidi_substream_t * substream, unsigned char midi_byte)
|
---|
620 | {
|
---|
621 | if (uart->buff_in_count == 0 /* Buffer empty? */
|
---|
622 | && (uart->adaptor != SNDRV_SERIAL_MS124W_SA ||
|
---|
623 | (uart->fifo_count == 0 /* FIFO empty? */
|
---|
624 | && (inb(uart->base + UART_MSR) & UART_MSR_CTS)))) { /* CTS? */
|
---|
625 |
|
---|
626 | /* Tx Buffer Empty - try to write immediately */
|
---|
627 | if ((inb(uart->base + UART_LSR) & UART_LSR_THRE) != 0) {
|
---|
628 | /* Transmitter holding register (and Tx FIFO) empty */
|
---|
629 | uart->fifo_count = 1;
|
---|
630 | outb(midi_byte, uart->base + UART_TX);
|
---|
631 | } else {
|
---|
632 | if (uart->fifo_count < uart->fifo_limit) {
|
---|
633 | uart->fifo_count++;
|
---|
634 | outb(midi_byte, uart->base + UART_TX);
|
---|
635 | } else {
|
---|
636 | /* Cannot write (buffer empty) - put char in buffer */
|
---|
637 | snd_uart16550_write_buffer(uart, midi_byte);
|
---|
638 | }
|
---|
639 | }
|
---|
640 | } else {
|
---|
641 | if (uart->buff_in_count >= TX_BUFF_SIZE) {
|
---|
642 | snd_printk("%s: Buffer overrun on device at 0x%lx\n",
|
---|
643 | uart->rmidi->name, uart->base);
|
---|
644 | return;
|
---|
645 | }
|
---|
646 | snd_uart16550_write_buffer(uart, midi_byte);
|
---|
647 | }
|
---|
648 | }
|
---|
649 |
|
---|
650 | static void snd_uart16550_output_write(snd_rawmidi_substream_t * substream)
|
---|
651 | {
|
---|
652 | unsigned long flags;
|
---|
653 | unsigned char midi_byte, addr_byte;
|
---|
654 | snd_uart16550_t *uart = substream->rmidi->private_data;
|
---|
655 | char first;
|
---|
656 |
|
---|
657 | /* Interupts are disabled during the updating of the tx_buff,
|
---|
658 | * since it is 'bad' to have two processes updating the same
|
---|
659 | * variables (ie buff_in & buff_out)
|
---|
660 | */
|
---|
661 |
|
---|
662 | spin_lock_irqsave(&uart->open_lock, flags);
|
---|
663 |
|
---|
664 | if (uart->irq < 0) //polling
|
---|
665 | snd_uart16550_io_loop(uart);
|
---|
666 |
|
---|
667 | if (uart->adaptor == SNDRV_SERIAL_MS124W_MB) {
|
---|
668 | while (1) {
|
---|
669 | /* buffer full? */
|
---|
670 | /* in this mode we need two bytes of space */
|
---|
671 | if (uart->buff_in_count > TX_BUFF_SIZE - 2)
|
---|
672 | break;
|
---|
673 | if (snd_rawmidi_transmit(substream, &midi_byte, 1) != 1)
|
---|
674 | break;
|
---|
675 | #if SNDRV_SERIAL_MS124W_MB_NOCOMBO
|
---|
676 | /* select exactly one of the four ports */
|
---|
677 | addr_byte = (1 << (substream->number + 4)) | 0x08;
|
---|
678 | #else
|
---|
679 | /* select any combination of the four ports */
|
---|
680 | addr_byte = (substream->number << 4) | 0x08;
|
---|
681 | /* ...except none */
|
---|
682 | if (addr_byte == 0x08) addr_byte = 0xf8;
|
---|
683 | #endif
|
---|
684 | snd_uart16550_output_byte(uart, substream, addr_byte);
|
---|
685 | /* send midi byte */
|
---|
686 | snd_uart16550_output_byte(uart, substream, midi_byte);
|
---|
687 | }
|
---|
688 | } else {
|
---|
689 | first = 0;
|
---|
690 | while (1) {
|
---|
691 | /* buffer full? */
|
---|
692 | if (uart->buff_in_count >= TX_BUFF_SIZE)
|
---|
693 | break;
|
---|
694 | if (snd_rawmidi_transmit(substream, &midi_byte, 1) != 1)
|
---|
695 | break;
|
---|
696 | if (first == 0 && uart->adaptor == SNDRV_SERIAL_SOUNDCANVAS &&
|
---|
697 | uart->prev_out != substream->number) {
|
---|
698 | /* Roland Soundcanvas part selection */
|
---|
699 | /* If this substream of the data is different previous
|
---|
700 | substream in this uart, send the change part event */
|
---|
701 | uart->prev_out = substream->number;
|
---|
702 | /* change part */
|
---|
703 | snd_uart16550_output_byte(uart, substream, 0xf5);
|
---|
704 | /* data */
|
---|
705 | snd_uart16550_output_byte(uart, substream, uart->prev_out + 1);
|
---|
706 | /* If midi_byte is a data byte, send the previous status byte */
|
---|
707 | if (midi_byte < 0x80)
|
---|
708 | snd_uart16550_output_byte(uart, substream, uart->prev_status[uart->prev_out]);
|
---|
709 | }
|
---|
710 | /* send midi byte */
|
---|
711 | snd_uart16550_output_byte(uart, substream, midi_byte);
|
---|
712 | if (midi_byte >= 0x80 && midi_byte < 0xf0)
|
---|
713 | uart->prev_status[uart->prev_out] = midi_byte;
|
---|
714 | first = 1;
|
---|
715 | }
|
---|
716 | }
|
---|
717 | spin_unlock_irqrestore(&uart->open_lock, flags);
|
---|
718 | }
|
---|
719 |
|
---|
720 | static void snd_uart16550_output_trigger(snd_rawmidi_substream_t * substream, int up)
|
---|
721 | {
|
---|
722 | unsigned long flags;
|
---|
723 | snd_uart16550_t *uart = substream->rmidi->private_data;
|
---|
724 |
|
---|
725 | spin_lock_irqsave(&uart->open_lock, flags);
|
---|
726 | if (up) {
|
---|
727 | uart->filemode |= SERIAL_MODE_OUTPUT_TRIGGERED;
|
---|
728 | } else {
|
---|
729 | uart->filemode &= ~SERIAL_MODE_OUTPUT_TRIGGERED;
|
---|
730 | }
|
---|
731 | spin_unlock_irqrestore(&uart->open_lock, flags);
|
---|
732 | if (up)
|
---|
733 | snd_uart16550_output_write(substream);
|
---|
734 | }
|
---|
735 |
|
---|
736 | #ifdef TARGET_OS2
|
---|
737 | static snd_rawmidi_ops_t snd_uart16550_output =
|
---|
738 | {
|
---|
739 | snd_uart16550_output_open,
|
---|
740 | snd_uart16550_output_close,
|
---|
741 | snd_uart16550_output_trigger,0
|
---|
742 | };
|
---|
743 |
|
---|
744 | static snd_rawmidi_ops_t snd_uart16550_input =
|
---|
745 | {
|
---|
746 | snd_uart16550_input_open,
|
---|
747 | snd_uart16550_input_close,
|
---|
748 | snd_uart16550_input_trigger,0
|
---|
749 | };
|
---|
750 | #else
|
---|
751 | static snd_rawmidi_ops_t snd_uart16550_output =
|
---|
752 | {
|
---|
753 | open: snd_uart16550_output_open,
|
---|
754 | close: snd_uart16550_output_close,
|
---|
755 | trigger: snd_uart16550_output_trigger,
|
---|
756 | };
|
---|
757 |
|
---|
758 | static snd_rawmidi_ops_t snd_uart16550_input =
|
---|
759 | {
|
---|
760 | open: snd_uart16550_input_open,
|
---|
761 | close: snd_uart16550_input_close,
|
---|
762 | trigger: snd_uart16550_input_trigger,
|
---|
763 | };
|
---|
764 | #endif
|
---|
765 |
|
---|
766 | static int snd_uart16550_free(snd_uart16550_t *uart)
|
---|
767 | {
|
---|
768 | if (uart->irq >= 0)
|
---|
769 | free_irq(uart->irq, (void *)uart);
|
---|
770 | if (uart->res_base)
|
---|
771 | release_resource(uart->res_base);
|
---|
772 | kfree(uart);
|
---|
773 | return 0;
|
---|
774 | };
|
---|
775 |
|
---|
776 | static int snd_uart16550_dev_free(snd_device_t *device)
|
---|
777 | {
|
---|
778 | snd_uart16550_t *uart = device->device_data;
|
---|
779 | return snd_uart16550_free(uart);
|
---|
780 | }
|
---|
781 |
|
---|
782 | static int __init snd_uart16550_create(snd_card_t * card,
|
---|
783 | unsigned long iobase,
|
---|
784 | int irq,
|
---|
785 | unsigned int speed,
|
---|
786 | unsigned int base,
|
---|
787 | int adaptor,
|
---|
788 | snd_uart16550_t **ruart)
|
---|
789 | {
|
---|
790 | #ifdef TARGET_OS2
|
---|
791 | static snd_device_ops_t ops = {
|
---|
792 | snd_uart16550_dev_free,0,0,0
|
---|
793 | };
|
---|
794 | #else
|
---|
795 | static snd_device_ops_t ops = {
|
---|
796 | dev_free: snd_uart16550_dev_free,
|
---|
797 | };
|
---|
798 | #endif
|
---|
799 | snd_uart16550_t *uart;
|
---|
800 | int err;
|
---|
801 |
|
---|
802 |
|
---|
803 | if ((uart = kcalloc(1, sizeof(*uart), GFP_KERNEL)) == NULL)
|
---|
804 | return -ENOMEM;
|
---|
805 | uart->adaptor = adaptor;
|
---|
806 | uart->card = card;
|
---|
807 | spin_lock_init(&uart->open_lock);
|
---|
808 | uart->irq = -1;
|
---|
809 | if ((uart->res_base = request_region(iobase, 8, "Serial MIDI")) == NULL) {
|
---|
810 | snd_printk("unable to grab ports 0x%lx-0x%lx\n", iobase, iobase + 8 - 1);
|
---|
811 | return -EBUSY;
|
---|
812 | }
|
---|
813 | uart->base = iobase;
|
---|
814 | if (irq >= 0) {
|
---|
815 | if (request_irq(irq, snd_uart16550_interrupt,
|
---|
816 | SA_INTERRUPT, "Serial MIDI", (void *) uart)) {
|
---|
817 | uart->irq = -1;
|
---|
818 | snd_printk("irq %d busy. Using Polling.\n", irq);
|
---|
819 | } else {
|
---|
820 | uart->irq = irq;
|
---|
821 | }
|
---|
822 | }
|
---|
823 | uart->divisor = base / speed;
|
---|
824 | uart->speed = base / (unsigned int)uart->divisor;
|
---|
825 | uart->speed_base = base;
|
---|
826 | uart->prev_out = -1;
|
---|
827 | memset(uart->prev_status, 0x80, sizeof(unsigned char) * SNDRV_SERIAL_MAX_OUTS);
|
---|
828 | uart->buffer_timer.function = snd_uart16550_buffer_timer;
|
---|
829 | uart->buffer_timer.data = (unsigned long)uart;
|
---|
830 | uart->timer_running = 0;
|
---|
831 |
|
---|
832 | /* Register device */
|
---|
833 | if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, uart, &ops)) < 0) {
|
---|
834 | snd_uart16550_free(uart);
|
---|
835 | return err;
|
---|
836 | }
|
---|
837 |
|
---|
838 | switch (uart->adaptor) {
|
---|
839 | case SNDRV_SERIAL_MS124W_SA:
|
---|
840 | case SNDRV_SERIAL_MS124W_MB:
|
---|
841 | /* MS-124W can draw power from RTS and DTR if they
|
---|
842 | are in opposite states. */
|
---|
843 | outb(UART_MCR_RTS | (0&UART_MCR_DTR), uart->base + UART_MCR);
|
---|
844 | break;
|
---|
845 | case SNDRV_SERIAL_MS124T:
|
---|
846 | /* MS-124T can draw power from RTS and/or DTR (preferably
|
---|
847 | both) if they are asserted. */
|
---|
848 | outb(UART_MCR_RTS | UART_MCR_DTR, uart->base + UART_MCR);
|
---|
849 | break;
|
---|
850 | default:
|
---|
851 | break;
|
---|
852 | }
|
---|
853 |
|
---|
854 | if (ruart)
|
---|
855 | *ruart = uart;
|
---|
856 |
|
---|
857 | return 0;
|
---|
858 | }
|
---|
859 |
|
---|
860 | static int __init snd_uart16550_rmidi(snd_uart16550_t *uart, int device, int outs, snd_rawmidi_t **rmidi)
|
---|
861 | {
|
---|
862 | snd_rawmidi_t *rrawmidi;
|
---|
863 | int err;
|
---|
864 |
|
---|
865 | if ((err = snd_rawmidi_new(uart->card, "UART Serial MIDI", device, outs, 1, &rrawmidi)) < 0)
|
---|
866 | return err;
|
---|
867 | snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_uart16550_input);
|
---|
868 | snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_uart16550_output);
|
---|
869 | sprintf(rrawmidi->name, "uart16550 MIDI #%d", device);
|
---|
870 | rrawmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
|
---|
871 | SNDRV_RAWMIDI_INFO_INPUT |
|
---|
872 | SNDRV_RAWMIDI_INFO_DUPLEX;
|
---|
873 | rrawmidi->private_data = uart;
|
---|
874 | if (rmidi)
|
---|
875 | *rmidi = rrawmidi;
|
---|
876 | return 0;
|
---|
877 | }
|
---|
878 |
|
---|
879 | static int __init snd_serial_probe(int dev)
|
---|
880 | {
|
---|
881 | snd_card_t *card;
|
---|
882 | snd_uart16550_t *uart;
|
---|
883 | int err;
|
---|
884 |
|
---|
885 | if (!snd_enable[dev])
|
---|
886 | return -ENOENT;
|
---|
887 |
|
---|
888 | switch (snd_adaptor[dev]) {
|
---|
889 | case SNDRV_SERIAL_SOUNDCANVAS:
|
---|
890 | break;
|
---|
891 | case SNDRV_SERIAL_MS124T:
|
---|
892 | case SNDRV_SERIAL_MS124W_SA:
|
---|
893 | snd_outs[dev] = 1;
|
---|
894 | break;
|
---|
895 | case SNDRV_SERIAL_MS124W_MB:
|
---|
896 | snd_outs[dev] = 16;
|
---|
897 | break;
|
---|
898 | default:
|
---|
899 | snd_printk("Adaptor type is out of range 0-%d (%d)\n",
|
---|
900 | SNDRV_SERIAL_MAX_ADAPTOR, snd_adaptor[dev]);
|
---|
901 | return -ENODEV;
|
---|
902 | }
|
---|
903 |
|
---|
904 | if (snd_outs[dev] < 1 || snd_outs[dev] > SNDRV_SERIAL_MAX_OUTS) {
|
---|
905 | snd_printk("Count of outputs is out of range 1-%d (%d)\n",
|
---|
906 | SNDRV_SERIAL_MAX_OUTS, snd_outs[dev]);
|
---|
907 | return -ENODEV;
|
---|
908 | }
|
---|
909 |
|
---|
910 | card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
|
---|
911 | if (card == NULL)
|
---|
912 | return -ENOMEM;
|
---|
913 |
|
---|
914 | strcpy(card->driver, "Serial");
|
---|
915 | strcpy(card->shortname, "Serial midi (uart16550A)");
|
---|
916 |
|
---|
917 | if ((err = snd_uart16550_detect(snd_port[dev])) <= 0) {
|
---|
918 | snd_card_free(card);
|
---|
919 | snd_printk("no UART detected at 0x%lx\n", (long)snd_port[dev]);
|
---|
920 | return err;
|
---|
921 | }
|
---|
922 |
|
---|
923 | if ((err = snd_uart16550_create(card,
|
---|
924 | snd_port[dev],
|
---|
925 | snd_irq[dev],
|
---|
926 | snd_speed[dev],
|
---|
927 | snd_base[dev],
|
---|
928 | snd_adaptor[dev],
|
---|
929 | &uart)) < 0) {
|
---|
930 | snd_card_free(card);
|
---|
931 | return err;
|
---|
932 | }
|
---|
933 |
|
---|
934 | if ((err = snd_uart16550_rmidi(uart, 0, snd_outs[dev], &uart->rmidi)) < 0) {
|
---|
935 | snd_card_free(card);
|
---|
936 | return err;
|
---|
937 | }
|
---|
938 |
|
---|
939 | sprintf(card->longname, "%s at 0x%lx, irq %d speed %d div %d outs %d adaptor %s",
|
---|
940 | card->shortname,
|
---|
941 | uart->base,
|
---|
942 | uart->irq,
|
---|
943 | uart->speed,
|
---|
944 | (int)uart->divisor,
|
---|
945 | snd_outs[dev],
|
---|
946 | adaptor_names[uart->adaptor]);
|
---|
947 |
|
---|
948 | if ((err = snd_card_register(card)) < 0) {
|
---|
949 | snd_card_free(card);
|
---|
950 | return err;
|
---|
951 | }
|
---|
952 | snd_serial_cards[dev] = card;
|
---|
953 | return 0;
|
---|
954 | }
|
---|
955 |
|
---|
956 | static int __init alsa_card_serial_init(void)
|
---|
957 | {
|
---|
958 | int dev = 0;
|
---|
959 | int cards = 0;
|
---|
960 |
|
---|
961 | for (dev = 0; dev < SNDRV_CARDS; dev++) {
|
---|
962 | if (snd_serial_probe(dev) == 0)
|
---|
963 | cards++;
|
---|
964 | }
|
---|
965 |
|
---|
966 | if (cards == 0) {
|
---|
967 | #ifdef MODULE
|
---|
968 | snd_printk("serial midi soundcard not found or device busy\n");
|
---|
969 | #endif
|
---|
970 | return -ENODEV;
|
---|
971 | }
|
---|
972 | return 0;
|
---|
973 | }
|
---|
974 |
|
---|
975 | static void __exit alsa_card_serial_exit(void)
|
---|
976 | {
|
---|
977 | int dev;
|
---|
978 |
|
---|
979 | for (dev = 0; dev < SNDRV_CARDS; dev++) {
|
---|
980 | if (snd_serial_cards[dev] != NULL)
|
---|
981 | snd_card_free(snd_serial_cards[dev]);
|
---|
982 | }
|
---|
983 | }
|
---|
984 |
|
---|
985 | module_init(alsa_card_serial_init)
|
---|
986 | module_exit(alsa_card_serial_exit)
|
---|
987 |
|
---|
988 | #ifndef MODULE
|
---|
989 |
|
---|
990 | /* format is: snd-card-serial=snd_enable,snd_index,snd_id,
|
---|
991 | snd_port,snd_irq,snd_speed,snd_base,snd_outs */
|
---|
992 |
|
---|
993 | static int __init alsa_card_serial_setup(char *str)
|
---|
994 | {
|
---|
995 | static unsigned __initdata nr_dev = 0;
|
---|
996 |
|
---|
997 | if (nr_dev >= SNDRV_CARDS)
|
---|
998 | return 0;
|
---|
999 | (void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
|
---|
1000 | get_option(&str,&snd_index[nr_dev]) == 2 &&
|
---|
1001 | get_id(&str,&snd_id[nr_dev]) == 2 &&
|
---|
1002 | get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
|
---|
1003 | get_option(&str,&snd_irq[nr_dev]) == 2 &&
|
---|
1004 | get_option(&str,&snd_speed[nr_dev]) == 2 &&
|
---|
1005 | get_option(&str,&snd_base[nr_dev]) == 2 &&
|
---|
1006 | get_option(&str,&snd_outs[nr_dev]) == 2 &&
|
---|
1007 | get_option(&str,&snd_adaptor[nr_dev]) == 2);
|
---|
1008 | nr_dev++;
|
---|
1009 | return 1;
|
---|
1010 | }
|
---|
1011 |
|
---|
1012 | __setup("snd-card-serial=", alsa_card_serial_setup);
|
---|
1013 |
|
---|
1014 | #endif /* ifndef MODULE */
|
---|