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 irqreturn_t 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 IRQ_HANDLED;
|
---|
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 | return IRQ_HANDLED;
|
---|
344 | }
|
---|
345 |
|
---|
346 | /* When the polling mode, this function calls snd_uart16550_io_loop. */
|
---|
347 | static void snd_uart16550_buffer_timer(unsigned long data)
|
---|
348 | {
|
---|
349 | snd_uart16550_t *uart;
|
---|
350 |
|
---|
351 | uart = (snd_uart16550_t *)data;
|
---|
352 | spin_lock(&uart->open_lock);
|
---|
353 | snd_uart16550_del_timer(uart);
|
---|
354 | snd_uart16550_io_loop(uart);
|
---|
355 | spin_unlock(&uart->open_lock);
|
---|
356 | }
|
---|
357 |
|
---|
358 | /*
|
---|
359 | * this method probes, if an uart sits on given port
|
---|
360 | * return 0 if found
|
---|
361 | * return negative error if not found
|
---|
362 | */
|
---|
363 | static int __init snd_uart16550_detect(unsigned int io_base)
|
---|
364 | {
|
---|
365 | int ok;
|
---|
366 | unsigned char c;
|
---|
367 |
|
---|
368 | if (check_region(io_base, 8))
|
---|
369 | return -EBUSY;
|
---|
370 |
|
---|
371 | /* Do some vague tests for the presence of the uart */
|
---|
372 | if (io_base == 0)
|
---|
373 | return -ENODEV; /* Not configured */
|
---|
374 |
|
---|
375 | ok = 1; /* uart detected unless one of the following tests should fail */
|
---|
376 | /* 8 data-bits, 1 stop-bit, parity off, DLAB = 0 */
|
---|
377 | outb(UART_LCR_WLEN8, io_base + UART_LCR); /* Line Control Register */
|
---|
378 | c = inb(io_base + UART_IER);
|
---|
379 | /* The top four bits of the IER should always == 0 */
|
---|
380 | if ((c & 0xf0) != 0)
|
---|
381 | ok = 0; /* failed */
|
---|
382 |
|
---|
383 | outb(0xaa, io_base + UART_SCR);
|
---|
384 | /* Write arbitrary data into the scratch reg */
|
---|
385 | c = inb(io_base + UART_SCR);
|
---|
386 | /* If it comes back, it's OK */
|
---|
387 | if (c != 0xaa)
|
---|
388 | ok = 0; /* failed */
|
---|
389 |
|
---|
390 | outb(0x55, io_base + UART_SCR);
|
---|
391 | /* Write arbitrary data into the scratch reg */
|
---|
392 | c = inb(io_base + UART_SCR);
|
---|
393 | /* If it comes back, it's OK */
|
---|
394 | if (c != 0x55)
|
---|
395 | ok = 0; /* failed */
|
---|
396 |
|
---|
397 | return ok;
|
---|
398 | }
|
---|
399 |
|
---|
400 | static void snd_uart16550_do_open(snd_uart16550_t * uart)
|
---|
401 | {
|
---|
402 | char byte;
|
---|
403 |
|
---|
404 | /* Initialize basic variables */
|
---|
405 | uart->buff_in_count = 0;
|
---|
406 | uart->buff_in = 0;
|
---|
407 | uart->buff_out = 0;
|
---|
408 | uart->fifo_limit = 1;
|
---|
409 | uart->fifo_count = 0;
|
---|
410 | uart->timer_running = 0;
|
---|
411 |
|
---|
412 | outb(UART_FCR_ENABLE_FIFO /* Enable FIFO's (if available) */
|
---|
413 | | UART_FCR_CLEAR_RCVR /* Clear receiver FIFO */
|
---|
414 | | UART_FCR_CLEAR_XMIT /* Clear transmitter FIFO */
|
---|
415 | | UART_FCR_TRIGGER_4 /* Set FIFO trigger at 4-bytes */
|
---|
416 | /* NOTE: interupt generated after T=(time)4-bytes
|
---|
417 | * if less than UART_FCR_TRIGGER bytes received
|
---|
418 | */
|
---|
419 | ,uart->base + UART_FCR); /* FIFO Control Register */
|
---|
420 |
|
---|
421 | if ((inb(uart->base + UART_IIR) & 0xf0) == 0xc0)
|
---|
422 | uart->fifo_limit = 16;
|
---|
423 | if (uart->divisor != 0) {
|
---|
424 | uart->old_line_ctrl_reg = inb(uart->base + UART_LCR);
|
---|
425 | outb(UART_LCR_DLAB /* Divisor latch access bit */
|
---|
426 | ,uart->base + UART_LCR); /* Line Control Register */
|
---|
427 | uart->old_divisor_lsb = inb(uart->base + UART_DLL);
|
---|
428 | uart->old_divisor_msb = inb(uart->base + UART_DLM);
|
---|
429 |
|
---|
430 | outb(uart->divisor
|
---|
431 | ,uart->base + UART_DLL); /* Divisor Latch Low */
|
---|
432 | outb(0
|
---|
433 | ,uart->base + UART_DLM); /* Divisor Latch High */
|
---|
434 | /* DLAB is reset to 0 in next outb() */
|
---|
435 | }
|
---|
436 | /* Set serial parameters (parity off, etc) */
|
---|
437 | outb(UART_LCR_WLEN8 /* 8 data-bits */
|
---|
438 | | 0 /* 1 stop-bit */
|
---|
439 | | 0 /* parity off */
|
---|
440 | | 0 /* DLAB = 0 */
|
---|
441 | ,uart->base + UART_LCR); /* Line Control Register */
|
---|
442 |
|
---|
443 | switch (uart->adaptor) {
|
---|
444 | default:
|
---|
445 | outb(UART_MCR_RTS /* Set Request-To-Send line active */
|
---|
446 | | UART_MCR_DTR /* Set Data-Terminal-Ready line active */
|
---|
447 | | UART_MCR_OUT2 /* Set OUT2 - not always required, but when
|
---|
448 | * it is, it is ESSENTIAL for enabling interrupts
|
---|
449 | */
|
---|
450 | ,uart->base + UART_MCR); /* Modem Control Register */
|
---|
451 | break;
|
---|
452 | case SNDRV_SERIAL_MS124W_SA:
|
---|
453 | case SNDRV_SERIAL_MS124W_MB:
|
---|
454 | /* MS-124W can draw power from RTS and DTR if they
|
---|
455 | are in opposite states. */
|
---|
456 | outb(UART_MCR_RTS | (0&UART_MCR_DTR) | UART_MCR_OUT2,
|
---|
457 | uart->base + UART_MCR);
|
---|
458 | break;
|
---|
459 | case SNDRV_SERIAL_MS124T:
|
---|
460 | /* MS-124T can draw power from RTS and/or DTR (preferably
|
---|
461 | both) if they are both asserted. */
|
---|
462 | outb(UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2,
|
---|
463 | uart->base + UART_MCR);
|
---|
464 | break;
|
---|
465 | }
|
---|
466 |
|
---|
467 | if (uart->irq < 0) {
|
---|
468 | byte = (0 & UART_IER_RDI) /* Disable Receiver data interupt */
|
---|
469 | |(0 & UART_IER_THRI) /* Disable Transmitter holding register empty interupt */
|
---|
470 | ;
|
---|
471 | } else if (uart->adaptor == SNDRV_SERIAL_MS124W_SA) {
|
---|
472 | byte = UART_IER_RDI /* Enable Receiver data interrupt */
|
---|
473 | | UART_IER_MSI /* Enable Modem status interrupt */
|
---|
474 | ;
|
---|
475 | } else {
|
---|
476 | byte = UART_IER_RDI /* Enable Receiver data interupt */
|
---|
477 | | UART_IER_THRI /* Enable Transmitter holding register empty interupt */
|
---|
478 | ;
|
---|
479 | }
|
---|
480 | outb(byte, uart->base + UART_IER); /* Interupt enable Register */
|
---|
481 |
|
---|
482 | inb(uart->base + UART_LSR); /* Clear any pre-existing overrun indication */
|
---|
483 | inb(uart->base + UART_IIR); /* Clear any pre-existing transmit interrupt */
|
---|
484 | inb(uart->base + UART_RX); /* Clear any pre-existing receive interrupt */
|
---|
485 | }
|
---|
486 |
|
---|
487 | static void snd_uart16550_do_close(snd_uart16550_t * uart)
|
---|
488 | {
|
---|
489 | if (uart->irq < 0)
|
---|
490 | snd_uart16550_del_timer(uart);
|
---|
491 |
|
---|
492 | /* NOTE: may need to disable interrupts before de-registering out handler.
|
---|
493 | * For now, the consequences are harmless.
|
---|
494 | */
|
---|
495 |
|
---|
496 | outb((0 & UART_IER_RDI) /* Disable Receiver data interupt */
|
---|
497 | |(0 & UART_IER_THRI) /* Disable Transmitter holding register empty interupt */
|
---|
498 | ,uart->base + UART_IER); /* Interupt enable Register */
|
---|
499 |
|
---|
500 | switch (uart->adaptor) {
|
---|
501 | default:
|
---|
502 | outb((0 & UART_MCR_RTS) /* Deactivate Request-To-Send line */
|
---|
503 | |(0 & UART_MCR_DTR) /* Deactivate Data-Terminal-Ready line */
|
---|
504 | |(0 & UART_MCR_OUT2) /* Deactivate OUT2 */
|
---|
505 | ,uart->base + UART_MCR); /* Modem Control Register */
|
---|
506 | break;
|
---|
507 | case SNDRV_SERIAL_MS124W_SA:
|
---|
508 | case SNDRV_SERIAL_MS124W_MB:
|
---|
509 | /* MS-124W can draw power from RTS and DTR if they
|
---|
510 | are in opposite states; leave it powered. */
|
---|
511 | outb(UART_MCR_RTS | (0&UART_MCR_DTR) | (0&UART_MCR_OUT2),
|
---|
512 | uart->base + UART_MCR);
|
---|
513 | break;
|
---|
514 | case SNDRV_SERIAL_MS124T:
|
---|
515 | /* MS-124T can draw power from RTS and/or DTR (preferably
|
---|
516 | both) if they are both asserted; leave it powered. */
|
---|
517 | outb(UART_MCR_RTS | UART_MCR_DTR | (0&UART_MCR_OUT2),
|
---|
518 | uart->base + UART_MCR);
|
---|
519 | break;
|
---|
520 | }
|
---|
521 |
|
---|
522 | inb(uart->base + UART_IIR); /* Clear any outstanding interupts */
|
---|
523 |
|
---|
524 | /* Restore old divisor */
|
---|
525 | if (uart->divisor != 0) {
|
---|
526 | outb(UART_LCR_DLAB /* Divisor latch access bit */
|
---|
527 | ,uart->base + UART_LCR); /* Line Control Register */
|
---|
528 | outb(uart->old_divisor_lsb
|
---|
529 | ,uart->base + UART_DLL); /* Divisor Latch Low */
|
---|
530 | outb(uart->old_divisor_msb
|
---|
531 | ,uart->base + UART_DLM); /* Divisor Latch High */
|
---|
532 | /* Restore old LCR (data bits, stop bits, parity, DLAB) */
|
---|
533 | outb(uart->old_line_ctrl_reg
|
---|
534 | ,uart->base + UART_LCR); /* Line Control Register */
|
---|
535 | }
|
---|
536 | }
|
---|
537 |
|
---|
538 | static int snd_uart16550_input_open(snd_rawmidi_substream_t * substream)
|
---|
539 | {
|
---|
540 | unsigned long flags;
|
---|
541 | snd_uart16550_t *uart = substream->rmidi->private_data;
|
---|
542 |
|
---|
543 | spin_lock_irqsave(&uart->open_lock, flags);
|
---|
544 | if (uart->filemode == SERIAL_MODE_NOT_OPENED)
|
---|
545 | snd_uart16550_do_open(uart);
|
---|
546 | uart->filemode |= SERIAL_MODE_INPUT_OPEN;
|
---|
547 | uart->midi_input = substream;
|
---|
548 | spin_unlock_irqrestore(&uart->open_lock, flags);
|
---|
549 | return 0;
|
---|
550 | }
|
---|
551 |
|
---|
552 | static int snd_uart16550_input_close(snd_rawmidi_substream_t * substream)
|
---|
553 | {
|
---|
554 | unsigned long flags;
|
---|
555 | snd_uart16550_t *uart = substream->rmidi->private_data;
|
---|
556 |
|
---|
557 | spin_lock_irqsave(&uart->open_lock, flags);
|
---|
558 | uart->filemode &= ~SERIAL_MODE_INPUT_OPEN;
|
---|
559 | uart->midi_input = NULL;
|
---|
560 | if (uart->filemode == SERIAL_MODE_NOT_OPENED)
|
---|
561 | snd_uart16550_do_close(uart);
|
---|
562 | spin_unlock_irqrestore(&uart->open_lock, flags);
|
---|
563 | return 0;
|
---|
564 | }
|
---|
565 |
|
---|
566 | static void snd_uart16550_input_trigger(snd_rawmidi_substream_t * substream, int up)
|
---|
567 | {
|
---|
568 | unsigned long flags;
|
---|
569 | snd_uart16550_t *uart = substream->rmidi->private_data;
|
---|
570 |
|
---|
571 | spin_lock_irqsave(&uart->open_lock, flags);
|
---|
572 | if (up) {
|
---|
573 | uart->filemode |= SERIAL_MODE_INPUT_TRIGGERED;
|
---|
574 | } else {
|
---|
575 | uart->filemode &= ~SERIAL_MODE_INPUT_TRIGGERED;
|
---|
576 | }
|
---|
577 | spin_unlock_irqrestore(&uart->open_lock, flags);
|
---|
578 | }
|
---|
579 |
|
---|
580 | static int snd_uart16550_output_open(snd_rawmidi_substream_t * substream)
|
---|
581 | {
|
---|
582 | unsigned long flags;
|
---|
583 | snd_uart16550_t *uart = substream->rmidi->private_data;
|
---|
584 |
|
---|
585 | spin_lock_irqsave(&uart->open_lock, flags);
|
---|
586 | if (uart->filemode == SERIAL_MODE_NOT_OPENED)
|
---|
587 | snd_uart16550_do_open(uart);
|
---|
588 | uart->filemode |= SERIAL_MODE_OUTPUT_OPEN;
|
---|
589 | uart->midi_output[substream->number] = substream;
|
---|
590 | spin_unlock_irqrestore(&uart->open_lock, flags);
|
---|
591 | return 0;
|
---|
592 | };
|
---|
593 |
|
---|
594 | static int snd_uart16550_output_close(snd_rawmidi_substream_t * substream)
|
---|
595 | {
|
---|
596 | unsigned long flags;
|
---|
597 | snd_uart16550_t *uart = substream->rmidi->private_data;
|
---|
598 |
|
---|
599 | spin_lock_irqsave(&uart->open_lock, flags);
|
---|
600 | uart->filemode &= ~SERIAL_MODE_OUTPUT_OPEN;
|
---|
601 | uart->midi_output[substream->number] = NULL;
|
---|
602 | if (uart->filemode == SERIAL_MODE_NOT_OPENED)
|
---|
603 | snd_uart16550_do_close(uart);
|
---|
604 | spin_unlock_irqrestore(&uart->open_lock, flags);
|
---|
605 | return 0;
|
---|
606 | };
|
---|
607 |
|
---|
608 | inline static void snd_uart16550_write_buffer(snd_uart16550_t *uart, unsigned char byte)
|
---|
609 | {
|
---|
610 | unsigned short buff_in = uart->buff_in;
|
---|
611 | uart->tx_buff[buff_in] = byte;
|
---|
612 | buff_in++;
|
---|
613 | buff_in &= TX_BUFF_MASK;
|
---|
614 | uart->buff_in = buff_in;
|
---|
615 | uart->buff_in_count++;
|
---|
616 | if (uart->irq < 0) /* polling mode */
|
---|
617 | snd_uart16550_add_timer(uart);
|
---|
618 | }
|
---|
619 |
|
---|
620 | static void snd_uart16550_output_byte(snd_uart16550_t *uart, snd_rawmidi_substream_t * substream, unsigned char midi_byte)
|
---|
621 | {
|
---|
622 | if (uart->buff_in_count == 0 /* Buffer empty? */
|
---|
623 | && (uart->adaptor != SNDRV_SERIAL_MS124W_SA ||
|
---|
624 | (uart->fifo_count == 0 /* FIFO empty? */
|
---|
625 | && (inb(uart->base + UART_MSR) & UART_MSR_CTS)))) { /* CTS? */
|
---|
626 |
|
---|
627 | /* Tx Buffer Empty - try to write immediately */
|
---|
628 | if ((inb(uart->base + UART_LSR) & UART_LSR_THRE) != 0) {
|
---|
629 | /* Transmitter holding register (and Tx FIFO) empty */
|
---|
630 | uart->fifo_count = 1;
|
---|
631 | outb(midi_byte, uart->base + UART_TX);
|
---|
632 | } else {
|
---|
633 | if (uart->fifo_count < uart->fifo_limit) {
|
---|
634 | uart->fifo_count++;
|
---|
635 | outb(midi_byte, uart->base + UART_TX);
|
---|
636 | } else {
|
---|
637 | /* Cannot write (buffer empty) - put char in buffer */
|
---|
638 | snd_uart16550_write_buffer(uart, midi_byte);
|
---|
639 | }
|
---|
640 | }
|
---|
641 | } else {
|
---|
642 | if (uart->buff_in_count >= TX_BUFF_SIZE) {
|
---|
643 | snd_printk("%s: Buffer overrun on device at 0x%lx\n",
|
---|
644 | uart->rmidi->name, uart->base);
|
---|
645 | return;
|
---|
646 | }
|
---|
647 | snd_uart16550_write_buffer(uart, midi_byte);
|
---|
648 | }
|
---|
649 | }
|
---|
650 |
|
---|
651 | static void snd_uart16550_output_write(snd_rawmidi_substream_t * substream)
|
---|
652 | {
|
---|
653 | unsigned long flags;
|
---|
654 | unsigned char midi_byte, addr_byte;
|
---|
655 | snd_uart16550_t *uart = substream->rmidi->private_data;
|
---|
656 | char first;
|
---|
657 |
|
---|
658 | /* Interupts are disabled during the updating of the tx_buff,
|
---|
659 | * since it is 'bad' to have two processes updating the same
|
---|
660 | * variables (ie buff_in & buff_out)
|
---|
661 | */
|
---|
662 |
|
---|
663 | spin_lock_irqsave(&uart->open_lock, flags);
|
---|
664 |
|
---|
665 | if (uart->irq < 0) //polling
|
---|
666 | snd_uart16550_io_loop(uart);
|
---|
667 |
|
---|
668 | if (uart->adaptor == SNDRV_SERIAL_MS124W_MB) {
|
---|
669 | while (1) {
|
---|
670 | /* buffer full? */
|
---|
671 | /* in this mode we need two bytes of space */
|
---|
672 | if (uart->buff_in_count > TX_BUFF_SIZE - 2)
|
---|
673 | break;
|
---|
674 | if (snd_rawmidi_transmit(substream, &midi_byte, 1) != 1)
|
---|
675 | break;
|
---|
676 | #if SNDRV_SERIAL_MS124W_MB_NOCOMBO
|
---|
677 | /* select exactly one of the four ports */
|
---|
678 | addr_byte = (1 << (substream->number + 4)) | 0x08;
|
---|
679 | #else
|
---|
680 | /* select any combination of the four ports */
|
---|
681 | addr_byte = (substream->number << 4) | 0x08;
|
---|
682 | /* ...except none */
|
---|
683 | if (addr_byte == 0x08) addr_byte = 0xf8;
|
---|
684 | #endif
|
---|
685 | snd_uart16550_output_byte(uart, substream, addr_byte);
|
---|
686 | /* send midi byte */
|
---|
687 | snd_uart16550_output_byte(uart, substream, midi_byte);
|
---|
688 | }
|
---|
689 | } else {
|
---|
690 | first = 0;
|
---|
691 | while (1) {
|
---|
692 | /* buffer full? */
|
---|
693 | if (uart->buff_in_count >= TX_BUFF_SIZE)
|
---|
694 | break;
|
---|
695 | if (snd_rawmidi_transmit(substream, &midi_byte, 1) != 1)
|
---|
696 | break;
|
---|
697 | if (first == 0 && uart->adaptor == SNDRV_SERIAL_SOUNDCANVAS &&
|
---|
698 | uart->prev_out != substream->number) {
|
---|
699 | /* Roland Soundcanvas part selection */
|
---|
700 | /* If this substream of the data is different previous
|
---|
701 | substream in this uart, send the change part event */
|
---|
702 | uart->prev_out = substream->number;
|
---|
703 | /* change part */
|
---|
704 | snd_uart16550_output_byte(uart, substream, 0xf5);
|
---|
705 | /* data */
|
---|
706 | snd_uart16550_output_byte(uart, substream, uart->prev_out + 1);
|
---|
707 | /* If midi_byte is a data byte, send the previous status byte */
|
---|
708 | if (midi_byte < 0x80)
|
---|
709 | snd_uart16550_output_byte(uart, substream, uart->prev_status[uart->prev_out]);
|
---|
710 | }
|
---|
711 | /* send midi byte */
|
---|
712 | snd_uart16550_output_byte(uart, substream, midi_byte);
|
---|
713 | if (midi_byte >= 0x80 && midi_byte < 0xf0)
|
---|
714 | uart->prev_status[uart->prev_out] = midi_byte;
|
---|
715 | first = 1;
|
---|
716 | }
|
---|
717 | }
|
---|
718 | spin_unlock_irqrestore(&uart->open_lock, flags);
|
---|
719 | }
|
---|
720 |
|
---|
721 | static void snd_uart16550_output_trigger(snd_rawmidi_substream_t * substream, int up)
|
---|
722 | {
|
---|
723 | unsigned long flags;
|
---|
724 | snd_uart16550_t *uart = substream->rmidi->private_data;
|
---|
725 |
|
---|
726 | spin_lock_irqsave(&uart->open_lock, flags);
|
---|
727 | if (up) {
|
---|
728 | uart->filemode |= SERIAL_MODE_OUTPUT_TRIGGERED;
|
---|
729 | } else {
|
---|
730 | uart->filemode &= ~SERIAL_MODE_OUTPUT_TRIGGERED;
|
---|
731 | }
|
---|
732 | spin_unlock_irqrestore(&uart->open_lock, flags);
|
---|
733 | if (up)
|
---|
734 | snd_uart16550_output_write(substream);
|
---|
735 | }
|
---|
736 |
|
---|
737 | #ifdef TARGET_OS2
|
---|
738 | static snd_rawmidi_ops_t snd_uart16550_output =
|
---|
739 | {
|
---|
740 | snd_uart16550_output_open,
|
---|
741 | snd_uart16550_output_close,
|
---|
742 | snd_uart16550_output_trigger,0
|
---|
743 | };
|
---|
744 |
|
---|
745 | static snd_rawmidi_ops_t snd_uart16550_input =
|
---|
746 | {
|
---|
747 | snd_uart16550_input_open,
|
---|
748 | snd_uart16550_input_close,
|
---|
749 | snd_uart16550_input_trigger,0
|
---|
750 | };
|
---|
751 | #else
|
---|
752 | static snd_rawmidi_ops_t snd_uart16550_output =
|
---|
753 | {
|
---|
754 | open: snd_uart16550_output_open,
|
---|
755 | close: snd_uart16550_output_close,
|
---|
756 | trigger: snd_uart16550_output_trigger,
|
---|
757 | };
|
---|
758 |
|
---|
759 | static snd_rawmidi_ops_t snd_uart16550_input =
|
---|
760 | {
|
---|
761 | open: snd_uart16550_input_open,
|
---|
762 | close: snd_uart16550_input_close,
|
---|
763 | trigger: snd_uart16550_input_trigger,
|
---|
764 | };
|
---|
765 | #endif
|
---|
766 |
|
---|
767 | static int snd_uart16550_free(snd_uart16550_t *uart)
|
---|
768 | {
|
---|
769 | if (uart->irq >= 0)
|
---|
770 | free_irq(uart->irq, (void *)uart);
|
---|
771 | if (uart->res_base)
|
---|
772 | release_resource(uart->res_base);
|
---|
773 | kfree(uart);
|
---|
774 | return 0;
|
---|
775 | };
|
---|
776 |
|
---|
777 | static int snd_uart16550_dev_free(snd_device_t *device)
|
---|
778 | {
|
---|
779 | snd_uart16550_t *uart = device->device_data;
|
---|
780 | return snd_uart16550_free(uart);
|
---|
781 | }
|
---|
782 |
|
---|
783 | static int __init snd_uart16550_create(snd_card_t * card,
|
---|
784 | unsigned long iobase,
|
---|
785 | int irq,
|
---|
786 | unsigned int speed,
|
---|
787 | unsigned int base,
|
---|
788 | int adaptor,
|
---|
789 | snd_uart16550_t **ruart)
|
---|
790 | {
|
---|
791 | #ifdef TARGET_OS2
|
---|
792 | static snd_device_ops_t ops = {
|
---|
793 | snd_uart16550_dev_free,0,0,0
|
---|
794 | };
|
---|
795 | #else
|
---|
796 | static snd_device_ops_t ops = {
|
---|
797 | dev_free: snd_uart16550_dev_free,
|
---|
798 | };
|
---|
799 | #endif
|
---|
800 | snd_uart16550_t *uart;
|
---|
801 | int err;
|
---|
802 |
|
---|
803 |
|
---|
804 | if ((uart = kcalloc(1, sizeof(*uart), GFP_KERNEL)) == NULL)
|
---|
805 | return -ENOMEM;
|
---|
806 | uart->adaptor = adaptor;
|
---|
807 | uart->card = card;
|
---|
808 | spin_lock_init(&uart->open_lock);
|
---|
809 | uart->irq = -1;
|
---|
810 | if ((uart->res_base = request_region(iobase, 8, "Serial MIDI")) == NULL) {
|
---|
811 | snd_printk("unable to grab ports 0x%lx-0x%lx\n", iobase, iobase + 8 - 1);
|
---|
812 | return -EBUSY;
|
---|
813 | }
|
---|
814 | uart->base = iobase;
|
---|
815 | if (irq >= 0) {
|
---|
816 | if (request_irq(irq, snd_uart16550_interrupt,
|
---|
817 | SA_INTERRUPT, "Serial MIDI", (void *) uart)) {
|
---|
818 | uart->irq = -1;
|
---|
819 | snd_printk("irq %d busy. Using Polling.\n", irq);
|
---|
820 | } else {
|
---|
821 | uart->irq = irq;
|
---|
822 | }
|
---|
823 | }
|
---|
824 | uart->divisor = base / speed;
|
---|
825 | uart->speed = base / (unsigned int)uart->divisor;
|
---|
826 | uart->speed_base = base;
|
---|
827 | uart->prev_out = -1;
|
---|
828 | memset(uart->prev_status, 0x80, sizeof(unsigned char) * SNDRV_SERIAL_MAX_OUTS);
|
---|
829 | uart->buffer_timer.function = snd_uart16550_buffer_timer;
|
---|
830 | uart->buffer_timer.data = (unsigned long)uart;
|
---|
831 | uart->timer_running = 0;
|
---|
832 |
|
---|
833 | /* Register device */
|
---|
834 | if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, uart, &ops)) < 0) {
|
---|
835 | snd_uart16550_free(uart);
|
---|
836 | return err;
|
---|
837 | }
|
---|
838 |
|
---|
839 | switch (uart->adaptor) {
|
---|
840 | case SNDRV_SERIAL_MS124W_SA:
|
---|
841 | case SNDRV_SERIAL_MS124W_MB:
|
---|
842 | /* MS-124W can draw power from RTS and DTR if they
|
---|
843 | are in opposite states. */
|
---|
844 | outb(UART_MCR_RTS | (0&UART_MCR_DTR), uart->base + UART_MCR);
|
---|
845 | break;
|
---|
846 | case SNDRV_SERIAL_MS124T:
|
---|
847 | /* MS-124T can draw power from RTS and/or DTR (preferably
|
---|
848 | both) if they are asserted. */
|
---|
849 | outb(UART_MCR_RTS | UART_MCR_DTR, uart->base + UART_MCR);
|
---|
850 | break;
|
---|
851 | default:
|
---|
852 | break;
|
---|
853 | }
|
---|
854 |
|
---|
855 | if (ruart)
|
---|
856 | *ruart = uart;
|
---|
857 |
|
---|
858 | return 0;
|
---|
859 | }
|
---|
860 |
|
---|
861 | static int __init snd_uart16550_rmidi(snd_uart16550_t *uart, int device, int outs, snd_rawmidi_t **rmidi)
|
---|
862 | {
|
---|
863 | snd_rawmidi_t *rrawmidi;
|
---|
864 | int err;
|
---|
865 |
|
---|
866 | if ((err = snd_rawmidi_new(uart->card, "UART Serial MIDI", device, outs, 1, &rrawmidi)) < 0)
|
---|
867 | return err;
|
---|
868 | snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_uart16550_input);
|
---|
869 | snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_uart16550_output);
|
---|
870 | sprintf(rrawmidi->name, "uart16550 MIDI #%d", device);
|
---|
871 | rrawmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
|
---|
872 | SNDRV_RAWMIDI_INFO_INPUT |
|
---|
873 | SNDRV_RAWMIDI_INFO_DUPLEX;
|
---|
874 | rrawmidi->private_data = uart;
|
---|
875 | if (rmidi)
|
---|
876 | *rmidi = rrawmidi;
|
---|
877 | return 0;
|
---|
878 | }
|
---|
879 |
|
---|
880 | static int __init snd_serial_probe(int dev)
|
---|
881 | {
|
---|
882 | snd_card_t *card;
|
---|
883 | snd_uart16550_t *uart;
|
---|
884 | int err;
|
---|
885 |
|
---|
886 | if (!snd_enable[dev])
|
---|
887 | return -ENOENT;
|
---|
888 |
|
---|
889 | switch (snd_adaptor[dev]) {
|
---|
890 | case SNDRV_SERIAL_SOUNDCANVAS:
|
---|
891 | break;
|
---|
892 | case SNDRV_SERIAL_MS124T:
|
---|
893 | case SNDRV_SERIAL_MS124W_SA:
|
---|
894 | snd_outs[dev] = 1;
|
---|
895 | break;
|
---|
896 | case SNDRV_SERIAL_MS124W_MB:
|
---|
897 | snd_outs[dev] = 16;
|
---|
898 | break;
|
---|
899 | default:
|
---|
900 | snd_printk("Adaptor type is out of range 0-%d (%d)\n",
|
---|
901 | SNDRV_SERIAL_MAX_ADAPTOR, snd_adaptor[dev]);
|
---|
902 | return -ENODEV;
|
---|
903 | }
|
---|
904 |
|
---|
905 | if (snd_outs[dev] < 1 || snd_outs[dev] > SNDRV_SERIAL_MAX_OUTS) {
|
---|
906 | snd_printk("Count of outputs is out of range 1-%d (%d)\n",
|
---|
907 | SNDRV_SERIAL_MAX_OUTS, snd_outs[dev]);
|
---|
908 | return -ENODEV;
|
---|
909 | }
|
---|
910 |
|
---|
911 | card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
|
---|
912 | if (card == NULL)
|
---|
913 | return -ENOMEM;
|
---|
914 |
|
---|
915 | strcpy(card->driver, "Serial");
|
---|
916 | strcpy(card->shortname, "Serial midi (uart16550A)");
|
---|
917 |
|
---|
918 | if ((err = snd_uart16550_detect(snd_port[dev])) <= 0) {
|
---|
919 | snd_card_free(card);
|
---|
920 | snd_printk("no UART detected at 0x%lx\n", (long)snd_port[dev]);
|
---|
921 | return err;
|
---|
922 | }
|
---|
923 |
|
---|
924 | if ((err = snd_uart16550_create(card,
|
---|
925 | snd_port[dev],
|
---|
926 | snd_irq[dev],
|
---|
927 | snd_speed[dev],
|
---|
928 | snd_base[dev],
|
---|
929 | snd_adaptor[dev],
|
---|
930 | &uart)) < 0) {
|
---|
931 | snd_card_free(card);
|
---|
932 | return err;
|
---|
933 | }
|
---|
934 |
|
---|
935 | if ((err = snd_uart16550_rmidi(uart, 0, snd_outs[dev], &uart->rmidi)) < 0) {
|
---|
936 | snd_card_free(card);
|
---|
937 | return err;
|
---|
938 | }
|
---|
939 |
|
---|
940 | sprintf(card->longname, "%s at 0x%lx, irq %d speed %d div %d outs %d adaptor %s",
|
---|
941 | card->shortname,
|
---|
942 | uart->base,
|
---|
943 | uart->irq,
|
---|
944 | uart->speed,
|
---|
945 | (int)uart->divisor,
|
---|
946 | snd_outs[dev],
|
---|
947 | adaptor_names[uart->adaptor]);
|
---|
948 |
|
---|
949 | if ((err = snd_card_register(card)) < 0) {
|
---|
950 | snd_card_free(card);
|
---|
951 | return err;
|
---|
952 | }
|
---|
953 | snd_serial_cards[dev] = card;
|
---|
954 | return 0;
|
---|
955 | }
|
---|
956 |
|
---|
957 | static int __init alsa_card_serial_init(void)
|
---|
958 | {
|
---|
959 | int dev = 0;
|
---|
960 | int cards = 0;
|
---|
961 |
|
---|
962 | for (dev = 0; dev < SNDRV_CARDS; dev++) {
|
---|
963 | if (snd_serial_probe(dev) == 0)
|
---|
964 | cards++;
|
---|
965 | }
|
---|
966 |
|
---|
967 | if (cards == 0) {
|
---|
968 | #ifdef MODULE
|
---|
969 | snd_printk("serial midi soundcard not found or device busy\n");
|
---|
970 | #endif
|
---|
971 | return -ENODEV;
|
---|
972 | }
|
---|
973 | return 0;
|
---|
974 | }
|
---|
975 |
|
---|
976 | static void __exit alsa_card_serial_exit(void)
|
---|
977 | {
|
---|
978 | int dev;
|
---|
979 |
|
---|
980 | for (dev = 0; dev < SNDRV_CARDS; dev++) {
|
---|
981 | if (snd_serial_cards[dev] != NULL)
|
---|
982 | snd_card_free(snd_serial_cards[dev]);
|
---|
983 | }
|
---|
984 | }
|
---|
985 |
|
---|
986 | module_init(alsa_card_serial_init)
|
---|
987 | module_exit(alsa_card_serial_exit)
|
---|
988 |
|
---|
989 | #ifndef MODULE
|
---|
990 |
|
---|
991 | /* format is: snd-card-serial=snd_enable,snd_index,snd_id,
|
---|
992 | snd_port,snd_irq,snd_speed,snd_base,snd_outs */
|
---|
993 |
|
---|
994 | static int __init alsa_card_serial_setup(char *str)
|
---|
995 | {
|
---|
996 | static unsigned __initdata nr_dev = 0;
|
---|
997 |
|
---|
998 | if (nr_dev >= SNDRV_CARDS)
|
---|
999 | return 0;
|
---|
1000 | (void)(get_option(&str,&snd_enable[nr_dev]) == 2 &&
|
---|
1001 | get_option(&str,&snd_index[nr_dev]) == 2 &&
|
---|
1002 | get_id(&str,&snd_id[nr_dev]) == 2 &&
|
---|
1003 | get_option(&str,(int *)&snd_port[nr_dev]) == 2 &&
|
---|
1004 | get_option(&str,&snd_irq[nr_dev]) == 2 &&
|
---|
1005 | get_option(&str,&snd_speed[nr_dev]) == 2 &&
|
---|
1006 | get_option(&str,&snd_base[nr_dev]) == 2 &&
|
---|
1007 | get_option(&str,&snd_outs[nr_dev]) == 2 &&
|
---|
1008 | get_option(&str,&snd_adaptor[nr_dev]) == 2);
|
---|
1009 | nr_dev++;
|
---|
1010 | return 1;
|
---|
1011 | }
|
---|
1012 |
|
---|
1013 | __setup("snd-card-serial=", alsa_card_serial_setup);
|
---|
1014 |
|
---|
1015 | #endif /* ifndef MODULE */
|
---|