summaryrefslogtreecommitdiff
path: root/kernel/framework/uart401/oss_uart401.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/framework/uart401/oss_uart401.c')
-rw-r--r--kernel/framework/uart401/oss_uart401.c340
1 files changed, 340 insertions, 0 deletions
diff --git a/kernel/framework/uart401/oss_uart401.c b/kernel/framework/uart401/oss_uart401.c
new file mode 100644
index 0000000..58945af
--- /dev/null
+++ b/kernel/framework/uart401/oss_uart401.c
@@ -0,0 +1,340 @@
+/*
+ * Purpose: MPU-401 (UART mode) driver
+ *
+ * This driver/library can be used by the drivers for devices that provide
+ * MPU-401 (UART) compatible interface.
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+#include "oss_config.h"
+#include "midi_core.h"
+
+#include "uart401.h"
+
+#define DATAPORT (devc->base)
+#define COMDPORT (devc->base+1)
+#define STATPORT (devc->base+1)
+
+static int
+uart401_status (uart401_devc * devc)
+{
+ return INB (devc->osdev, STATPORT);
+}
+
+#define input_avail(devc) (!(uart401_status(devc)&INPUT_AVAIL))
+#define output_ready(devc) (!(uart401_status(devc)&OUTPUT_READY))
+static void
+uart401_cmd (uart401_devc * devc, unsigned char cmd)
+{
+ OUTB (devc->osdev, cmd, COMDPORT);
+}
+static int
+uart401_read (uart401_devc * devc)
+{
+ return INB (devc->osdev, DATAPORT);
+}
+static void
+uart401_write (uart401_devc * devc, unsigned char byte)
+{
+ OUTB (devc->osdev, byte, DATAPORT);
+}
+
+#define OUTPUT_READY 0x40
+#define INPUT_AVAIL 0x80
+#define MPU_ACK 0xFE
+#define MPU_RESET 0xFF
+#define UART_MODE_ON 0x3F
+
+static int reset_uart401 (uart401_devc * devc);
+static void enter_uart_mode (uart401_devc * devc);
+
+static void
+uart401_input_loop (uart401_devc * devc)
+{
+ unsigned char buf[128];
+ int l = 0;
+
+ while (input_avail (devc))
+ {
+ int dev;
+ unsigned char c;
+
+ c = uart401_read (devc);
+
+ dev = devc->my_dev;
+ if (midi_devs[dev]->input_callback != NULL)
+ midi_devs[dev]->input_callback (dev, c);
+
+ if (c == MPU_ACK)
+ devc->input_byte = c;
+ else if ((devc->opened & OPEN_READ))
+ {
+ buf[l++] = c;
+
+ if (l >= sizeof (buf)) /* Buffer full */
+ {
+ if (devc->save_input_buffer != NULL)
+ devc->save_input_buffer (devc->my_dev, buf, l);
+ l = 0;
+ }
+ }
+
+ if (l > 0)
+ {
+ if (devc->save_input_buffer != NULL)
+ devc->save_input_buffer (devc->my_dev, buf, l);
+ }
+ }
+}
+
+void
+uart401_irq (uart401_devc * devc)
+{
+ if (devc->base == 0)
+ {
+ /* cmn_err(CE_CONT, "uart401_irq: Bad base address\n"); */
+ return;
+ }
+
+ if (input_avail (devc))
+ {
+ uart401_input_loop (devc);
+ }
+}
+
+/*ARGSUSED*/
+static int
+uart401_open (int dev, int mode, oss_midi_inputbyte_t inputbyte,
+ oss_midi_inputbuf_t inputbuf, oss_midi_outputintr_t outputintr)
+{
+ uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc;
+
+ if (devc->opened)
+ {
+ return OSS_EBUSY;
+ }
+
+ while (input_avail (devc))
+ uart401_read (devc);
+
+ devc->save_input_buffer = inputbuf;
+ devc->opened = mode;
+ enter_uart_mode (devc);
+ devc->disabled = 0;
+
+ return 0;
+}
+
+/*ARGSUSED*/
+static void
+uart401_close (int dev, int mode)
+{
+ uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc;
+
+ reset_uart401 (devc);
+ oss_udelay (10);
+ enter_uart_mode (devc);
+ reset_uart401 (devc);
+ devc->save_input_buffer = NULL;
+ devc->opened = 0;
+}
+
+static int
+uart401_out (int dev, unsigned char midi_byte)
+{
+ int timeout;
+ uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc;
+ oss_native_word flags;
+
+ if (devc->disabled)
+ return 1;
+ /*
+ * Test for input since pending input seems to block the output.
+ */
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+
+ if (input_avail (devc))
+ uart401_input_loop (devc);
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ /*
+ * Sometimes it takes about 130000 loops before the output becomes ready
+ * (After reset). Normally it takes just about 10 loops.
+ */
+
+ for (timeout = 13000; timeout > 0 && !output_ready (devc); timeout--);
+
+ if (!output_ready (devc))
+ {
+ return 0;
+ }
+
+ uart401_write (devc, midi_byte);
+ return 1;
+}
+
+/*ARGSUSED*/
+static int
+uart401_ioctl (int dev, unsigned cmd, ioctl_arg arg)
+{
+ return OSS_EINVAL;
+}
+
+static midi_driver_t uart401_driver = {
+ uart401_open,
+ uart401_close,
+ uart401_ioctl,
+ uart401_out
+};
+
+static void
+enter_uart_mode (uart401_devc * devc)
+{
+ int ok, timeout;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ for (timeout = 30000; timeout > 0 && !output_ready (devc); timeout--);
+
+ devc->input_byte = 0;
+ uart401_cmd (devc, UART_MODE_ON);
+
+ ok = 0;
+ for (timeout = 50000; timeout > 0 && !ok; timeout--)
+ if (devc->input_byte == MPU_ACK)
+ ok = 1;
+ else if (input_avail (devc))
+ if (uart401_read (devc) == MPU_ACK)
+ ok = 1;
+
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+}
+
+static int
+reset_uart401 (uart401_devc * devc)
+{
+ int ok, timeout, n;
+ oss_native_word flags;
+
+ /*
+ * Send the RESET command. Try again if no success at the first time.
+ */
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ ok = 0;
+
+ for (n = 0; n < 2 && !ok; n++)
+ {
+ for (timeout = 30000; timeout > 0 && !output_ready (devc); timeout--);
+
+ devc->input_byte = 0;
+ uart401_cmd (devc, MPU_RESET);
+
+ /*
+ * Wait at least 25 msec. This method is not accurate so let's make the
+ * loop bit longer. Cannot sleep since this is called during boot.
+ */
+
+ for (timeout = 50000; timeout > 0 && !ok; timeout--)
+ if (devc->input_byte == MPU_ACK) /* Interrupt */
+ ok = 1;
+ else if (input_avail (devc))
+ if (uart401_read (devc) == MPU_ACK)
+ ok = 1;
+
+ }
+
+
+
+ if (ok)
+ uart401_input_loop (devc); /*
+ * Flush input before enabling interrupts
+ */
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return ok;
+}
+
+int
+uart401_init (uart401_devc * devc, oss_device_t * osdev, int base, char *name)
+{
+ int ok = 0;
+
+ DDB (cmn_err (CE_CONT, "Entered uart401_init(%x)\n", base));
+
+ devc->base = base;
+ devc->irq = 0;
+ devc->osdev = osdev;
+ MUTEX_INIT (devc->osdev, devc->mutex, MH_DRV + 3);
+ devc->running = 1;
+
+ devc->save_input_buffer = NULL;
+ devc->opened = 0;
+ devc->input_byte = 0;
+ devc->my_dev = 0;
+ devc->share_irq = 0;
+
+ ok = reset_uart401 (devc);
+
+ if (ok)
+ {
+ DDB (cmn_err (CE_CONT, "Reset UART401 OK\n"));
+ }
+ else
+ {
+ DDB (cmn_err
+ (CE_CONT, "Reset UART401 failed (no hardware present?).\n"));
+ DDB (cmn_err (CE_CONT, "mpu401 status %02x\n", uart401_status (devc)));
+ }
+
+ if (!ok)
+ {
+ return 0;
+ }
+
+ DDB (cmn_err (CE_CONT, "uart401 detected OK\n"));
+
+ enter_uart_mode (devc);
+
+ devc->my_dev = oss_install_mididev (OSS_MIDI_DRIVER_VERSION, "UART401", name, &uart401_driver, sizeof (midi_driver_t),
+ 0, devc, devc->osdev);
+ devc->opened = 0;
+#ifdef USE_POLLING
+ if (!timer_armed)
+ {
+ timer_armed = 1;
+ poll_interval = OSS_HZ / 10;
+ if (poll_interval < 1)
+ poll_interval = 1;
+ INIT_TIMER (uart401_timer, uart401_poll);
+ ACTIVATE_TIMER (uart401_timer, uart401_poll, poll_interval);
+ }
+#endif
+ return 1;
+}
+
+void
+uart401_disable (uart401_devc * devc)
+{
+#ifdef USE_POLLING
+ if (timer_armed)
+ {
+ timer_armed = 0;
+ REMOVE_TIMER (uart401_timer, uart401_poll);
+ }
+ else;
+#endif
+ if (!devc->running)
+ return;
+ reset_uart401 (devc);
+ devc->running = 0;
+}