The latest HTML version of this document is available from: http://crystal.apana.org.au/ghansper/midi/uart16550_midi.html
Contents
The file uart16550_midi.c contains a low-level driver which makes MIDI devices out of normal serial-port devices. The driver is implemented as a low-level driver for the OSS-Free Linux sound driver.
Files included:
uart16550_midi.html - these instructions
uart16550_midi.linux-2.0.x.patch - patches for the 2.0.x kernel sound driver
uart16550_midi.linux-2.2.x.patch - patches for the 2.2.x kernel sound driver
uart16550_midi.linux-2.4.x.patch - patches for the 2.4.x kernel sound driver
uart16550_midi.c - the actual code for the driver
dev_table.c.linux-2.0.x.diffs - only required if you don't configure
any other soundcard
Configure.help.uart16550_midi - addendum for linux/Documentation/Configure.help
Artistic.txt - Copyright/Licensing information
This driver supports UART devices of the types 16450, 16550, and 16552. The 16552 is a dual 16550, and will be detected as a 16550 device.
This driver is useful if you have one of the following configurations:
Commands that you type appear like this
You must be the user root to install this driver.
tar kxvfz uart16550_midi.4b.tar.gz -C /usr/src/linux/drivers/sound/lowlevel
cd /usr/src/linux/drivers/sound/lowlevel
patch --backup < uart16550_midi.linux-2.0.x.patch
patch --backup ../dev_table.c < dev_table.c.linux-2.0.x.diffs
tar kxvfz uart16550_midi.4b.tar.gz -C /usr/src/linux/drivers/sound/lowlevel
cd /usr/src/linux/drivers/sound/lowlevel
patch --backup < uart16550_midi.linux-2.2.x.patch
tar kxvfz uart16550_midi.4b.tar.gz -C /usr/src/linux/drivers/sound
cd /usr/src/linux/drivers/sound
patch --backup < uart16550_midi.linux-2.4.x.patch
gcc -o sndstat sndstat.c
cat Configure.help.uart16550_midi >> /usr/src/linux/Documentation/Configure.help
cd /usr/src/linux
make xconfig
make menuconfig
In the section "Sound", enable the following options. You can use either Y or M (M is preferrable).
make dep clean bzImage modules modules_install
lilo
/etc/conf.modules (for example)
options sound trace_init=-1 io1=0x2e8
/etc/conf.modules
alias sound uart16550_midi options uart16550_midi io1=0x2e8 # The next line is usually built-in to modprobe... alias char-major-14 sound
Example 2 - together with a Sound Blaster card
alias sound sb post-install sb /sbin/modprobe "-k" "uart16550_midi" options uart16550_midi io1=0x2e8 # The next line is usually built-in to modprobe... alias char-major-14 sound
depmod -a
depmod -a is always run automatically at bootup)
cat /dev/sndstat OSS/Free:3.8s2++-971130 Load type: Driver loaded as a module Kernel: Linux dilithium 2.2.9 #7 Tue Aug 10 12:56:10 /etc/localtime 1999 i586 Config options: 0 Installed drivers: Card config: Audio devices: Synth devices: Midi devices: 0: uart16550_midi 0x02e8 Timers: 0: System clock Mixers: playmidi -e -D 0 some_midi_file.mid ...MIDI noise happens...
/usr/src/linux/drivers/sound/sndstat OSS/Free version 3.8.2 Config options: Unknown Installed drivers: Card config: Audio devices: Synth devices: MIDI devices: 0: uart16550_midi 0x02f8 Timers: No information available Mixer devices: playmidi -e -D 0 some_midi_file.mid ...MIDI noise happens...
When you configure the kernel with this driver, the following information will prove useful:
An IRQ of -1 tells the driver to auto-probe for the IRQ. This may not work if you are trying to share interrupts with another device.
An IRQ of 0 tells the driver to use polling rather than interrupts, at some cost of performance.
A divisor of -1 tells the driver to auto-probe for the divisor. It will attempt to set 31250 baud if possible, or 38400 baud as a fall-back.
A divisor of >0 will be used explictly. This only required if you need a different baud-rate, or are having problems with the auto-detection.
A divisor of 0 tells the driver to leave the divisor unchanged from the last value set by the regular tty driver.
This driver now does auto-probing of both the IRQ and divisor. All you really need to know is the IO base-address of the serial port(s).
The base address and IRQ are typically set by jumpers or dip-switches on the IO card. The usual values for these are:
| Serial port | ttyS0 | ttyS1 | ttyS2 | ttyS3 |
|---|---|---|---|---|
| DOS name | COM1 | COM2 | COM3 | COM4 |
| Base Address | 3f8 | 2f8 | 3e8 | 2e8 |
| IRQ | 4 | 3 | 5,7 or 9 | 5,7 or 9 |
Although this driver supports sharing of IRQ's, usually
the hardware does not allow this.
One notable exception is the Winman2x2 which uses a
single IRQ between its two MIDI devices.
Sharing IRQ's between devices on different ISA-bus cards is not possible in practice. PCI cards normally allow IRQ sharing.
This version of the UART16550 MIDI driver will set the divisor automatically. A baud rate of 31250 is standard for MIDI. However, most PC serial ports are incapable of this baud rate, so the driver will attempt to set 38400 as a fall-back position.
If you need a different baud-rate, or auto-probing is not working for you, you can explicity set the divisor. This can be done during configuration, or when the module is loaded.
The baud-rate of the 16450 and 16550 UARTs is determined by
baud_rate = UART_clock_frequency / (16 x UART_divisor )
where the UART_clock_frequency is not the CPU clock, but
and independant clock used to drive the UART.
The UART clock frequency may be determined by examining the IO card. The frequency is normally written on the crystal which is used regulate the clock oscillator. The crystal is easy to identify, as it is usually the only component in a metal can.
Some typical values for the UART_clock_frequency are:
The correct baud-rate for MIDI is 31250 baud, and the divisor must be set accordingly. ie
UART_divisor = UART_clock_frequency / ( 16 x baud_rate ) (rounded to the nearest integer)
A normal PC serial port has a 1.8432 MHz UART clock, which cannot be used to achieve the required MIDI baud rate. A divisor of 3 gives 38400 baud, which may be acceptable to some MIDI devices.
The base address and IRQ of the Winman1x1 and Winman2x2 are configurable (by jumpers) over the range:
| Winman 1x1 | Winman 2x2 | |
|---|---|---|
| Base Address | 0x200-0x278, 0x300-0x378
| 0x200-0x270, 0x300-0x370
0x208-0x278, 0x308-0x378
|
in steps of 8
| in steps of 0x10
| |
| IRQ | 3,4,5,7,9,10,11,12,15
| |
| Divisor | 12
| |
The Winman2x2 consists of 2x 16550 serial ports, and both may be used by this software driver. The second base address is always +8 above the first, and both ports use the same IRQ.
The Winman1x1 and Winman2x2 MIDI devices use a 6MHz UART clock, and hence require a divisor of 12.
For example, if base-address 0x200 has been set on the Winman2x2, then the 1st device is at base address 0x200, and the 2nd is at base address 0x208.
PLEASE NOTE
This is a low-level driver which writes directly to the serial-port hardware.
>> THERE IS NO PROTECTION AGAINST CONFLICTS
>> with the regular TTY driver
The consequences of such conflicts are not severe. If you're not careful, you'll send MIDI data to your modem or mouse, or inadvertently take your modem or MIDI device off-line.
I'll be happy to add such protection, if someone can give me some idea of how to go about this.
In the mean time, it is important to:
This will eliminate 99% of possible problems
/dev/midinn /dev/sequencer /dev/sequencer2 /dev/musicdo not access the /dev/cuan or /dev/ttySn devices which correspond to the /dev/midinn devices (and vice-versa). Note that opening:
/dev/sequencerfor reading opens ALL of the /dev/midinn devices simultaneously.
If the driver is compiled as a module, it supports the following module-parameters:
| IO Base address | Interrupt vector (IRQ) | Divisor | |
|---|---|---|---|
| 1st device | io1=iobase | irq1=irq | div1=div |
| 2nd device | io2=iobase | irq2=irq | div2=div |
| 3rd device | io3=iobase | irq3=irq | div3=div |
| 4th device | io4=iobase | irq4=irq | div4=div |
| Default value | 0 | -1 | -1 |
Up to 4 devices can be configured.
The driver reports all devices which it detects with a message like this:
<uart 16550A Midi Interface midi00> io=0x2E8, irq=7, div=16 (31250 baud)
Check that the details look right, in particular the irq and div values.
You'll get one such message for each device detected and configured.
If you don't see this message, use the command dmesg
to review the latest kernel messages.
dmesg(8) reprints kernel messages, which are kept in a 'ring'
buffer.
This specifies the IO base address of the serial port. The driver will only use those serial ports which are explicity configured.
io1 != 0
io parameter looks for a suitable UART
at the given address. Usually specified as a hexadecimal number,
eg io1=0x3e8
io1=0
io2=0 disables the
2nd device.
If you are compiling the driver as a module, you can specify 0 for all base addresses during configuration,
and use the module-parameters later.
eg io1=... io2=...
irq1= 1...15
irq1=0
irq1=-1
div1= 2...48
baud_rate = UART_clock_frequency / (16 x UART_divisor )
The data-sheets on the UARTs say the minimum divisor is 2. Sensible values are in the range 2...48, although this is a 16-bit number.
div1=0
setserial and stty .
div1=-1
If you are using the kerneld or kernel module loader (Linux 2.2.x and later),
module parameters can be specified in the file /etc/conf.modules
See the section "Installation Instructions"
for examples of how to edit /etc/conf.modules
If you are using insmod to load the module manually,
you can add the module parameters to the end of the command line, like this:
insmod sound io1=0x200 div1=12 io2=0x208 div2=12
insmod soundlow insmod soundcore insmod sound insmod uart16550_midi io1=0x3e8 io2=0x2e8 irq2=0
If you specified a divisor of zero during configuration the divisor will remain unchanged from the previous value when the MIDI device is opened and closed.
In this case you must set the appropriate divisor
using the commands stty and setserial.
This is best done during boot-up (eg in /etc/rc.d/rc.serial)
Example 1:
stty 38400 < /dev/ttyS2
Sets 38400 baud on a normal serial-port driven by a
1.8432 MHz crystal oscillator
Example 2:
setserial /dev/ttyS2 divisor 16 spd_cust baud_base 500000
stty 38400 < /dev/ttyS2
Sets 31250 baud on a non-standard serial-port driven by an
8 MHz crystal oscillator.
Example 3:
setserial /dev/ttyS5 port 0x320 irq 11 divisor 12 spd_cust baud_base 375000
stty 38400 < /dev/ttyS5
setserial /dev/ttyS6 port 0x328 irq 11 divisor 12 spd_cust baud_base 375000
stty 38400 < /dev/ttyS6
Sets 31250 baud on each device of a Winman2x2 MIDI interface, which
is driven by a 6 MHz crystal oscillator and shares a single IRQ between
both devices.
Suggested solutions, anyone?
When sharing an ISA interrupt with another device driver, occasionally the interrupt gets "hung", stopping both this MIDI driver and the other device. Closing the MIDI device file clears the problem.
sound=...
to the driver is gone.