Linux sound driver for 16450 and 16550A UARTS

The latest HTML version of this document is available from: http://crystal.apana.org.au/ghansper/midi/uart16550_midi.html

Contents


Introduction

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.

Features

Manifest

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

Requirements

Software Requirements

Hardware Requirements

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:

  1. a sound-module or keyboard with a 38400 baud serial port, or

  2. A 16550-based MIDI interface card, such as the Winman1x1 or Winman2x2, or

  3. a)
    One or more serial ports capable of 31250 baud, +/- 3%
    b)
    A suitable adaptor to convert the voltage levels and provide electrical isolation.
    Instructions for modifying an ordinary serial-card to support 31250 baud, and a design for a suitable adaptor can obtained from:
    http://crystal.apana.org.au/~ghansper/midiaxis.html

Installation Instructions

Commands that you type appear like this

You must be the user root to install this driver.

    1. If you are using Linux 2.0.x kernel:
      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
    2. If you are using Linux 2.2.x kernel:
      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
    3. If you are using Linux 2.4.x kernel:
      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

  1.  cat Configure.help.uart16550_midi >> /usr/src/linux/Documentation/Configure.help

  2.  cd /usr/src/linux

  3. Configure your kernel as usual:
    make xconfig
    or
    make menuconfig
    If you haven't done this before, see /usr/doc/HOWTO/Kernel-HOWTO . gz

    In the section "Sound", enable the following options. You can use either Y or M (M is preferrable).

    1. Linux 2.0.x kernel:
      • Enable "Sound card support"
      • Enable "MIDI interface support"
      • Enable "Additional low level drivers"
      • Enable "Serial (16450/16550A) MIDI driver"

    2. Linux 2.2.x kernel:
      • Enable "Sound card support"
      • Enable "OSS sound modules"
      • Enable "Additional low level drivers"
      • Enable "Serial (16450/16550A) MIDI driver"

    3. Linux 2.4.x kernel:
      • Enable "Sound card support"
      • Enable "OSS sound modules"
      • Enable "Serial (16450/16550A) MIDI driver for OSS"

  4. make dep clean bzImage modules modules_install

  5. Install the new kernel, as usual
    Don't forget to run lilo

    1. Linux 2.0.x kernel:
      • Add to /etc/conf.modules (for example)
        options sound trace_init=-1 io1=0x2e8
        
    2. Linux 2.2.x and 2.4.x kernels:
      • Add to /etc/conf.modules
        Example 1 - stand-alone, no other sound-card
        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
        
  6. depmod -a
    or reboot using the new kernel
    (depmod -a is always run automatically at bootup)

  7. Testing:
    1. Linux 2.0.x and 2.2.x kernels:
      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...
      
    2. Linux 2.4.x kernels:
      /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...
      

    Configuration Information

    Configuration Parameters and their Default Values

    When you configure the kernel with this driver, the following information will prove useful:

    1. The IO base address of the 16450 or 16550 device(s) which you intend the use as midi device(s).
      This is essential information

    2. The IRQ which each device is to use

      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.

    3. The divisor which is required to give 31250 baud

      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).

    Base Address and IRQ

    The base address and IRQ are typically set by jumpers or dip-switches on the IO card. The usual values for these are:
    PC Serial Ports
    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.

    UART Clock Frequency and divisor

    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.

    Winman1x1 and Winman2x2 Base Address, IRQ and Divisor

    The base address and IRQ of the Winman1x1 and Winman2x2 are configurable (by jumpers) over the range:
    Winman 2x2 and Winman 1x1
      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.


    Using the Driver

    Possible Conflict with /dev/ttySn or /dev/cuan

    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:

    a)
    During configuration or module loading, only nominate those serial ports which REALLY ARE going to be used as MIDI devices

    This will eliminate 99% of possible problems

    b)
    While you are using:
    	/dev/midinn
    	/dev/sequencer
    	/dev/sequencer2
    	/dev/music
    
    do not access the /dev/cuan or /dev/ttySn devices which correspond to the /dev/midinn devices (and vice-versa). Note that opening:
       	/dev/sequencer
    
    for reading opens ALL of the /dev/midinn devices simultaneously.

    Module Parameters

    If the driver is compiled as a module, it supports the following module-parameters:
    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.

    The IO parameter (io1...io4)

    This specifies the IO base address of the serial port. The driver will only use those serial ports which are explicity configured.

    io1 != 0
    A non-zero io parameter looks for a suitable UART at the given address. Usually specified as a hexadecimal number, eg io1=0x3e8

    io1=0
    A base-address of 0 disables the nth device. eg 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=... 

    The IRQ parameter (irq1...irq4)

    irq1= 1...15
    This specifies the IRQ (interrupt vector) of the serial port. Normally, this will be a value from the set (3,4,5,7,9,10,11,12). Other values in the range 0-15 are used by "standard" devices, like the keyboard and floppy disk.

    irq1=0
    This tell the driver to use polling instead of an interrupt. The device will be checked every 10ms for new data. On a 16450, this delay is long enough to cause overruns. The 16-byte FIFO buffer in the 16550 is usually sufficient to avoid overruns.

    irq1=-1
    This tells the driver to probe for the interrupt.

    Sharing IRQ's with other devices
    The driver is capable of sharing an interrupt with another device, which may or may not be another uart16550_midi device. If the device sharing the IRQ is not another uart16550_midi device, auto-probing of the IRQ is likely to fail. In this case, it is necessary to specify the irq parameter explicity.

    The divisor parameter (div1 ... div4)

    div1= 2...48
    This specifies the divisor, which determines the baud rate. The baud rate is given by the equation:
    	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
    This tells the driver to leave the divisor untouched from the exisiting value. This assumes that you have set up the divisor previously using the commands setserial and stty .

    div1=-1
    This tells the driver to probe for the divisor. Probing is pretty reliable, but not perfect.

    Using Module Parameters

    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:

    Linux 2.0.x
    insmod sound io1=0x200 div1=12 io2=0x208 div2=12
    
    Linux 2.2.x and 2.4.x
    insmod soundlow
    insmod soundcore
    insmod sound
    insmod uart16550_midi io1=0x3e8 io2=0x2e8 irq2=0
    

    Setting the divisor externally (div1=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.

    Known Problems and Bugs

    Changes as of version 4b

    Changes as of version 4a

    Changes as of version 354-3a

    Bugs fixed as of version 351-2b

    Bugs fixed as of version 301-1c


    Comments, Questions, Feedback, Suggestions, are all welcome

    George Hansper
    George_Hansper@apana.org.au
    http://crystal.apana.org.au/ghansper/
    Last Modified: Mon Apr 2 21:26:23 EST 2001