patch-2.2.18 linux/drivers/char/atari_MIDI.c
Next file: linux/drivers/char/atari_MIDI.h
Previous file: linux/drivers/char/atari_MFPser.h
Back to the patch index
Back to the overall index
- Lines: 568
- Date:
Sat Oct 14 00:06:42 2000
- Orig file:
v2.2.17/drivers/char/atari_MIDI.c
- Orig date:
Thu Jan 1 01:00:00 1970
diff -u --new-file --recursive --exclude-from /usr/src/exclude v2.2.17/drivers/char/atari_MIDI.c linux/drivers/char/atari_MIDI.c
@@ -0,0 +1,567 @@
+/*
+ * drivers/char/atari_MIDI.c: Atari MIDI driver as serial port
+ *
+ * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+ * Partially based on PC-Linux serial.c by Linus Torvalds and Theodore Ts'o
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ * Modified for midi by Martin Schaller
+ */
+
+
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/termios.h>
+#include <linux/m68kserial.h>
+#include <linux/init.h>
+
+#include <asm/traps.h>
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+#include <asm/atarikb.h>
+
+#include "atari_MIDI.h"
+
+
+/* Defines */
+
+/* #define _DEBUG_MIDI_ */
+
+#define DEFAULT_MIDI_LINE 5 /* ttyS5 */
+#define MIDI_BAUD_BASE 500000 /* 31250 */
+
+#define STOP_SHIFT 0
+#define PAR_SHIFT 4
+#define CS_SHIFT 8
+
+/* Prototypes */
+static void MIDI_init_port(struct m68k_async_struct* info, int type, int tt_flag);
+static void MIDI_int(void); /* called from keyboard int handler */
+static void MIDI_init( struct m68k_async_struct * info );
+static void MIDI_deinit( struct m68k_async_struct * info, int leave_dtr );
+static void MIDI_enab_tx_int( struct m68k_async_struct * info, int enab_flag );
+static int MIDI_check_custom_divisor(struct m68k_async_struct* info, int baud_base, int divisor);
+static void MIDI_change_speed( struct m68k_async_struct * info );
+static void MIDI_throttle( struct m68k_async_struct * info, int status );
+static void MIDI_set_break( struct m68k_async_struct * info, int break_flag );
+static unsigned int MIDI_get_modem_info( struct m68k_async_struct * info );
+static void MIDI_get_serial_info( struct m68k_async_struct* info, struct serial_struct* retinfo);
+static int MIDI_set_modem_info( struct m68k_async_struct* info, int new_dtr, int new_rts );
+static void MIDI_stop_receive(struct m68k_async_struct * info);
+static int MIDI_trans_empty(struct m68k_async_struct * info);
+
+
+struct m68k_async_struct* midi_info;
+static unsigned char mid_ctrl_shadow; /* Bernd Harries, 960525 */
+static unsigned char mid_stat_shadow; /* Bernd Harries, 961207 */
+
+
+#ifdef _BHA_MIDI_CHAR_FMT
+static int MIDI_char_fmt[8] = {
+ (CS7 << CS_SHIFT) | (PARENB << PAR_SHIFT) | (2 << STOP_SHIFT),
+ (CS7 << CS_SHIFT) | ((PARENB | PARODD) << PAR_SHIFT) | (2 << STOP_SHIFT),
+ (CS7 << CS_SHIFT) | (PARENB << PAR_SHIFT) | (1 << STOP_SHIFT),
+ (CS7 << CS_SHIFT) | ((PARENB | PARODD) << PAR_SHIFT) | (1 << STOP_SHIFT),
+ (CS8 << CS_SHIFT) | (0 << PAR_SHIFT) | (2 << STOP_SHIFT),
+ (CS8 << CS_SHIFT) | (0 << PAR_SHIFT) | (1 << STOP_SHIFT),
+ (CS8 << CS_SHIFT) | (PARENB << PAR_SHIFT) | (1 << STOP_SHIFT),
+ (CS8 << CS_SHIFT) | ((PARENB | PARODD) << PAR_SHIFT) | (1 << STOP_SHIFT),
+};
+#endif
+
+ /* index = P * 4 + L * 2 + S * 1 */
+ /* S = (2 == Stopbits) */
+ /* L = (8 == Databits) */
+ /* P = Parity {Odd, Even, None} */
+
+static char ACIA_char_fmt[16] = {
+ ACIA_D7O1S,
+ ACIA_D7O2S,
+ ACIA_D8O1S,
+ -1,
+ ACIA_D7E1S,
+ ACIA_D7E2S,
+ ACIA_D8E1S,
+ -1,
+ -1,
+ -1,
+ ACIA_D8N1S,
+ ACIA_D8N2S,
+ -2
+};
+
+/* Divisors for standard speeds & RSVE Bernd Harries 961127 */
+static int MIDI_baud_table[20] = {
+ /* B0 */ 0, /* invalid */
+ /* B50 */ 0,
+ /* B75 */ 0,
+ /* B110 */ 0,
+ /* B134 */ 0,
+ /* B150 */ 0,
+ /* B200 */ 0,
+ /* B300 */ 0,
+ /* B600 */ 0,
+ /* B1200 */ 0,
+ /* B1800 */ 0,
+ /* B2400 */ 0,
+ /* B4800 */ 0, /* invalid */
+ /* B9600 */ 64, /* really 7812.5 bps */
+ /* B19200 */ 0, /* invalid */
+ /* B38400 */ 16, /* really 31250 bps */
+ /* B57600 */ 0, /* invalid */
+ /* B115200 */ 0, /* invalid */
+ /* B230400 */ 0, /* invalid */
+ /* B460800 */ 1 /* really 500000 bps */
+};
+
+ /* The following 2 arrays must be congruent! */
+static int ACIA_prescaler_modes[] = { 1, 16, 64 };
+static char ACIA_baud_masks[] = { ACIA_DIV1, ACIA_DIV16, ACIA_DIV64 };
+
+ /*
+ * SERIALSWITCH structures for MIDI port
+ */
+
+static SERIALSWITCH MIDI_switch = {
+ MIDI_init,
+ MIDI_deinit,
+ MIDI_enab_tx_int,
+ MIDI_check_custom_divisor,
+ MIDI_change_speed,
+ MIDI_throttle,
+ MIDI_set_break,
+ MIDI_get_serial_info,
+ MIDI_get_modem_info,
+ MIDI_set_modem_info,
+ NULL, /* MIDI_ioctl, */
+ MIDI_stop_receive,
+ MIDI_trans_empty,
+ NULL /* MIDI_check_open */
+};
+
+
+__initfunc(int atari_MIDI_init( void ))
+{
+ extern char m68k_debug_device[];
+ static struct serial_struct req;
+ int midi_line;
+ int nr = 0;
+
+#ifdef _DEBUG_MIDI_
+ printk(" atari_MIDI_init() \n");
+#endif
+
+ if (!strcmp( m68k_debug_device, "midi" ))
+ printk(KERN_NOTICE "MIDI serial port used as debug device\n" );
+ else {
+ req.line = DEFAULT_MIDI_LINE;
+ req.type = SER_MIDI;
+ req.port = (int) &acia.mid_ctrl;
+
+ midi_line = register_serial( &req );
+ if (midi_line >= 0) {
+ MIDI_init_port(&rs_table[midi_line], req.type, 0);
+ ++nr;
+ }
+ else {
+ printk(KERN_WARNING "Cannot allocate ttyS%d for MIDI\n", req.line );
+ }
+ }
+ return( nr > 0 ? 0 : -ENODEV );
+}
+
+
+static void MIDI_init_port(struct m68k_async_struct * info, int type, int tt_flag)
+{
+ midi_info = info; /* modulglobal !!!!! */
+
+ info->sw = &MIDI_switch;
+ info->custom_divisor = 16; /* 31250 Baud */
+ info->baud_base = MIDI_BAUD_BASE;
+ info->sw = &MIDI_switch;
+
+
+ /* set ISRs, but don't enable interrupts yet (done in init());
+ * all ints are choosen of type FAST, and they're really quite fast.
+ * Furthermore, we have to account for the fact that these are three ints,
+ * and one can interrupt another. So better protect them against one
+ * another...
+ */
+ /*
+ request_irq(IRQ_MIDI_SEREMPT, MIDI_tx_int, IRQ_TYPE_FAST, "MIDI TX", info);
+ request_irq(IRQ_MIDI_RECFULL, MIDI_rx_int, IRQ_TYPE_FAST, "MIDI RX", info);
+ request_irq(IRQ_MIDI_RECERR,
+ MIDI_rxerr_int,
+ IRQ_TYPE_FAST,
+ "MIDI RX error",
+ info);
+ */
+
+ /* Tx_err interrupt unused (it signals only that the Tx shift reg
+ * is empty)
+ */
+ /* Leave RTS high for now if selected as a switch, so that it's still valid
+ * as long as the port isn't opened.
+ */
+ mid_ctrl_shadow = ACIA_DIV16 | ACIA_D8N1S |
+ ((atari_switches&ATARI_SWITCH_MIDI) ?
+ ACIA_RHTID : ACIA_RLTID);
+ acia.mid_ctrl = mid_ctrl_shadow;
+
+ atari_MIDI_interrupt_hook = MIDI_int;
+}
+
+
+static void MIDI_int(void) /* called from keyboard int handler */
+{
+ static int err;
+/* register int stat; */
+ register int ch;
+
+ mid_stat_shadow = acia.mid_ctrl;
+
+ /* if == Rx Data Reg Full -> Interrupt */
+ if (mid_stat_shadow & (ACIA_RDRF | ACIA_FE | ACIA_OVRN)) {
+ ch = acia.mid_data;
+ err = 0;
+ if(mid_stat_shadow & ACIA_FE) err = TTY_FRAME;
+ if(mid_stat_shadow & ACIA_OVRN) err = TTY_OVERRUN;
+ rs_receive_char(midi_info, ch, err);
+ /* printk("R"); */
+ }
+
+ if (acia.mid_ctrl & ACIA_TDRE) { /* Tx Data Reg Empty Transmit Interrupt */
+ ch = rs_get_tx_char( midi_info );
+
+ if (ch >= 0) { /* 32 Bit */
+ acia.mid_data = ch;
+ /* printk("_%c", ch); */
+ }
+
+ if (ch == -1 || rs_no_more_tx( midi_info )) {
+ /* disable tx interrupts %x01xxxxx ==> %x00xxxxx */
+ /* RTS Low, Tx IRQ Disabled */
+ mid_ctrl_shadow &= ~ACIA_RLTIE;
+ acia.mid_ctrl = mid_ctrl_shadow;
+ /* printk("T"); */
+ }
+ }
+}
+
+static void MIDI_init( struct m68k_async_struct * info )
+{
+#ifdef _DEBUG_MIDI_
+ printk(" MIDI_init() \n");
+#endif
+ /* Baud = DIV16, 8N1, denable rx interrupts */
+ mid_ctrl_shadow = ACIA_DIV16 | ACIA_D8N1S | ACIA_RIE;
+ acia.mid_ctrl = mid_ctrl_shadow;
+
+ MOD_INC_USE_COUNT;
+}
+
+static void MIDI_deinit( struct m68k_async_struct *info, int leave_dtr )
+{
+#ifdef _DEBUG_MIDI_
+ printk(" MIDI_deinit() \n");
+#endif
+
+ /* seems like the status register changes on read */
+#ifdef _MIDI_WAIT_FOR_TX_EMPTY_
+ while(!(mid_stat_shadow & ACIA_TDRE)) { /* wait for TX empty */
+ ;
+ /* printk("m"); */
+ }
+#endif
+
+ /* Baud = DIV16, 8N1, disable Rx and Tx interrupts */
+ mid_ctrl_shadow = ACIA_DIV16 | ACIA_D8N1S;
+ acia.mid_ctrl = mid_ctrl_shadow;
+
+ /* read Rx status and data to clean up */
+ (void)acia.mid_ctrl;
+ (void)acia.mid_data;
+
+ MOD_DEC_USE_COUNT;
+}
+
+
+/*
+ * ACIA Control Register can only be written! Read accesses Status Register!
+ * Shadowing may be nescessary here like for SCC
+ *
+ * Bernd Harries, 960525 Tel.: +49-421-804309
+ * harries@asrv01.atlas.de
+ * Bernd_Harries@hb2.maus.de
+ */
+
+static void MIDI_enab_tx_int( struct m68k_async_struct * info, int enab_flag )
+{
+ unsigned long cpu_status;
+
+ if (enab_flag) {
+ register int ch;
+
+ save_flags(cpu_status);
+ cli();
+ /* RTS Low, Tx IRQ Enabled */
+ mid_ctrl_shadow |= ACIA_RLTIE;
+ acia.mid_ctrl = mid_ctrl_shadow;
+ /* restarted the transmitter */
+ /*
+ * These extensions since 0.9.5 are only allowed, if the
+ * Tx Data Register is Empty!
+ * In 1.2.13pl10 this did not work. So I added the if().
+ *
+ * Bernd Harries 960530
+ * harries@atlas.de
+ * harries@asrv01.atlas.de
+ * Bernd_Harries@hb2.maus.de
+ */
+ if (acia.mid_ctrl & ACIA_TDRE) { /* If last Char since disabling is gone */
+ ch = rs_get_tx_char( midi_info );
+ if (ch >= 0) {
+ acia.mid_data = ch;
+ /* printk("=%c", ch); */
+ }
+
+ if (ch == -1 || rs_no_more_tx( midi_info )) {
+ /* disable tx interrupts */
+ /* RTS Low, Tx IRQ Disabled */
+ mid_ctrl_shadow &= ~ACIA_RLTIE;
+ acia.mid_ctrl = mid_ctrl_shadow;
+
+ }
+ }
+ restore_flags(cpu_status);
+ } else {
+ save_flags(cpu_status);
+ cli();
+ /* RTS Low, Tx IRQ Disabled */
+ mid_ctrl_shadow &= ~ACIA_RLTIE;
+ acia.mid_ctrl = mid_ctrl_shadow;
+ restore_flags(cpu_status);
+ }
+}
+
+
+static int MIDI_check_custom_divisor(struct m68k_async_struct* info, int baud_base, int divisor)
+{
+#ifdef _DEBUG_MIDI_
+ printk(" MIDI_check_custom_divisor() \n");
+#endif
+
+ if (baud_base != MIDI_BAUD_BASE) return -1;
+
+ /* divisor must be a multiple of 1, 16, 64 */
+
+ switch (divisor) {
+ case 1:
+ case 16:
+ case 64: return(0);
+ }
+
+ return(-1);
+}
+
+
+static void MIDI_change_speed( struct m68k_async_struct *info )
+{
+ unsigned long cpu_status;
+ unsigned int baud, stopb, parity, aflags;
+ unsigned int div, cflag, chsize;
+ int timer_mode;
+ int index;
+
+ unsigned char mid_ctrl_new;
+ unsigned char baud_mask;
+ unsigned char chsize_mask;
+ unsigned char fmt_mask;
+
+#ifdef _DEBUG_MIDI_
+ printk(" MIDI_change_speed() \n");
+#endif
+
+ div = 0;
+ cflag = info->tty->termios->c_cflag;
+ baud = cflag & CBAUD;
+ chsize = cflag & CSIZE;
+ stopb = cflag & CSTOPB;
+ parity = cflag & (PARENB | PARODD);
+ aflags = info->flags & ASYNC_SPD_MASK;
+
+ if (cflag & CRTSCTS)
+ info->flags |= ASYNC_CTS_FLOW;
+ else
+ info->flags &= ~ASYNC_CTS_FLOW;
+
+ if (cflag & CLOCAL)
+ info->flags &= ~ASYNC_CHECK_CD;
+ else
+ info->flags |= ASYNC_CHECK_CD;
+
+ if (baud & CBAUDEX) {
+ baud &= ~CBAUDEX;
+ if (baud < 1 || baud > 4)
+ info->tty->termios->c_cflag &= ~CBAUDEX;
+ else
+ baud += 15;
+ }
+ if (baud == 15) {
+ switch (aflags) {
+ case ASYNC_SPD_HI:
+ baud = 16; /* 134 Baud, with RSVE = 57600 */
+ break;
+ case ASYNC_SPD_VHI:
+ baud = 17; /* 150 Baud, with RSVE = 115200 */
+ break;
+ case ASYNC_SPD_SHI:
+ baud = 18;
+ break;
+ case ASYNC_SPD_WARP:
+ baud = 19;
+ break;
+ case ASYNC_SPD_CUST:
+ div = info->custom_divisor;
+ break;
+ }
+ }
+
+ if (!div) {
+ /* Maximum MIDI speed is 500000 */
+ if (baud > 19) baud = 19;
+ div = MIDI_baud_table[baud];
+ }
+
+ if (div) {
+ /* turn on DTR */
+ /* MIDI_DTRon(); */
+ } else {
+ /* speed == 0 -> drop DTR */
+ /* MIDI_DTRoff(); */
+ return;
+ }
+
+ mid_ctrl_new = 0;
+ for (timer_mode = 2; timer_mode >= 0; timer_mode--)
+ if (ACIA_prescaler_modes[timer_mode] == div)
+ break;
+
+ baud_mask = ACIA_baud_masks[timer_mode];
+
+ chsize_mask = 0;
+ if (chsize == CS8) chsize_mask = ACIA_D8N2S;
+
+ index = 0;
+ if (stopb) index |= (1 << 0);
+ if (chsize == CS8) index |= (1 << 1);
+ if (parity == 0)
+ index |= (2 << 2);
+ else if (parity == PARENB)
+ index |= (1 << 2);
+ else { /* if(parity == PARENB | PARODD) */
+ /* index |= (0 << 2); */
+ }
+
+ fmt_mask = ACIA_char_fmt[index];
+
+ /* Now we have all parameters and can go to set them: */
+ save_flags(cpu_status);
+ cli();
+
+ /* disable Rx and Tx while changing parameters
+ * stop timer D to set new timer value immediatly after re-enabling
+ */
+ mid_ctrl_shadow &= ~ACIA_RESET; /* MASK_OUT significant bits */
+ mid_ctrl_shadow |= baud_mask;
+ mid_ctrl_shadow &= ~ACIA_D8O1S;
+ mid_ctrl_shadow |= fmt_mask;
+
+ acia.mid_ctrl = mid_ctrl_shadow; /* Write only */
+ restore_flags(cpu_status);
+
+#ifdef _DEBUG_MIDI_
+ printk(" MIDI_change_speed() done. \n");
+#endif
+}
+
+
+static void MIDI_throttle( struct m68k_async_struct * info, int status )
+{
+ if (status) ; /* MIDI_RTSoff(); */
+ else ; /* MIDI_RTSon(); */
+}
+
+
+static void MIDI_set_break( struct m68k_async_struct * info, int break_flag )
+{
+}
+
+
+static unsigned int MIDI_get_modem_info( struct m68k_async_struct *info )
+{
+ return( TIOCM_RTS | TIOCM_DTR | TIOCM_CAR | TIOCM_CTS | TIOCM_DSR );
+}
+
+
+static void MIDI_get_serial_info( struct m68k_async_struct* info, struct serial_struct* retinfo )
+{
+#ifdef _DEBUG_MIDI_
+ printk(" MIDI_get_serial_info() \n");
+#endif
+
+ retinfo->baud_base = info->baud_base;
+ retinfo->custom_divisor = info->custom_divisor;
+}
+
+
+static int MIDI_set_modem_info( struct m68k_async_struct *info, int new_dtr, int new_rts )
+{
+ /* Is it right to return an error or should the attempt to change
+ * DTR or RTS be silently ignored?
+ */
+ return( -EINVAL );
+}
+
+
+static void MIDI_stop_receive (struct m68k_async_struct *info)
+{
+ unsigned long cpu_status;
+
+ save_flags(cpu_status);
+ cli();
+
+ /* disable receive interrupts */
+ mid_ctrl_shadow &= ~ACIA_RIE;
+ acia.mid_ctrl = mid_ctrl_shadow;
+ restore_flags(cpu_status);
+}
+
+
+static int MIDI_trans_empty (struct m68k_async_struct *info)
+{
+ return (acia.mid_ctrl & ACIA_TDRE) != 0;
+}
+
+
+#ifdef MODULE
+int init_module(void)
+{
+ return( atari_MIDI_init() );
+}
+
+void cleanup_module(void)
+{
+ atari_MIDI_interrupt_hook = NULL;
+ unregister_serial( midi_info->line );
+}
+#endif
+
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)