home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.ee.lbl.gov
/
2014.05.ftp.ee.lbl.gov.tar
/
ftp.ee.lbl.gov
/
bmd-1.0beta.tar.Z
/
bmd-1.0beta.tar
/
bmd-1.0beta
/
sundev
/
zs_midi.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-08-27
|
9KB
|
452 lines
/*
* Copyright (c) 1990 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Lawrence Berkeley Laboratory,
* Berkeley, CA. The name of the University may not be used to
* endorse or promote products derived from this software without
* specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Low level (one mdq at a time) tap to 8530 UART.
* Works by stealing the chip away from the zs async driver.
* Too bad zsopinit() doesn't do proper resource bookeeping,
* otherwise things would work correctly.
*/
#include "bmd.h"
#if NBMD > 0
#ifndef lint
static char rcsid[] =
"@(#) $Header: zs_midi.c,v 1.11 91/08/27 00:37:20 mccanne Exp $";
#endif
#include <sys/systm.h>
#include <sys/param.h>
/*
* Sun mixed software-only data structures with hardware register
* definitions in zsreg.h. This was a very bad idea. Instead of
* including a bunch of streams crap for struct zsaline in zsreg.h,
* we define away the offending type names. (None of the code below
* references a zsaline).
*/
#define mblk_t void*
#define tty_common_t void*
#include <sundev/zsreg.h>
#include <sundev/zscom.h>
#include <sundev/midi.h>
#include <sundev/bmdvar.h>
#include <sundev/zs_midi.h>
static int zsmidi_txint();
static int zsmidi_xsint();
static int zsmidi_rxint();
static int zsmidi_srint();
static int
zsmidi_null()
{
return 0;
}
struct zsops zsops_hook = {
zsmidi_null,
zsmidi_txint,
zsmidi_xsint,
zsmidi_rxint,
zsmidi_srint,
zsmidi_null,
};
#define ZMFC_NONE 0
#define ZMFC_SW 1
#define ZMFC_HW 2
#ifndef MIDIRATE
#define MIDIRATE 31250
#endif
#if MIDIRATE == 31250
int zsmidi_fc = ZMFC_NONE;
#else
int zsmidi_fc = ZMFC_HW;
#endif
struct zsmidi {
struct zscom *zm_common; /* address of zs common data struct */
caddr_t zm_private; /* midi softc pointer */
struct mdq *zm_qh; /* output queue head */
struct mdq *zm_qt; /* output queue tail */
int zm_speed;
u_char zm_busy;
u_char zm_status;
u_char zm_sleep;
u_char zm_fc;
/*
* Various stats, mainly for debugging.
*/
int zm_pe;
int zm_overrun;
int zm_txdrops;
};
#define NPORTS 2
struct zsmidi zsmidi[NPORTS];
void
msl_disconnect(arg, drain)
void *arg;
int drain;
{
struct zsmidi *zm = (struct zsmidi *)arg;
register struct zscom *zs = zm->zm_common;
if (zm->zm_qh != 0) {
if (drain) {
zm->zm_sleep = 1;
(void)sleep((caddr_t)zm, (PZERO+1)|PCATCH);
zm->zm_sleep = 0;
}
mdqfreeq(zm->zm_qh, zm->zm_qt);
zm->zm_qt = zm->zm_qh = 0;
}
ZBIC(1, ZSWR1_RIE);
}
#include <machine/param.h>
#ifdef OPENPROMS
extern struct zscom *zscom;
#else
extern struct zscom zscom[];
#endif
extern int hz;
static void
msl_watchdog(arg)
caddr_t arg;
{
((struct zsmidi *)arg)->zm_status = 0;
timeout((int (*)())msl_watchdog, arg, 3 * hz);
}
/*
* This is probably 31.25kb, but it might be 38400.
*/
int midi_speed = MIDIRATE;
void *
msl_init(unit, private)
int unit;
caddr_t private;
{
int s;
struct zsmidi *zm = &zsmidi[unit];
register struct zscom *zs = &zscom[unit];
zm->zm_common = zs;
zm->zm_private = private;
zm->zm_qh = 0;
zm->zm_qt = 0;
zm->zm_speed = ZSTimeConst(4915200, midi_speed);
zm->zm_busy = 0;
zm->zm_status = 0;
zm->zm_sleep = 0;
zm->zm_fc = zsmidi_fc;
zm->zm_pe = 0;
zm->zm_overrun = 0;
zm->zm_txdrops = 0;
s = splzs();
zs->zs_priv = (caddr_t)zm;
/*
* This call will grab the chip, even if something else has it.
*/
zsopinit(zs, &zsops_hook);
msl_chipinit(zm);
(void)splx(s);
/*
* Start up a watch dog to reset the running status every
* three seconds, so we can move midi cables and power up
* devices.
*/
timeout((int (*)())msl_watchdog, (caddr_t)zm, 3 * hz);
return (void *)zm;
}
void
msl_setspeed(arg, speed)
void *arg;
int speed;
{
int s;
struct zsmidi *zm = (struct zsmidi *)arg;
s = splmidi();
zm->zm_speed = speed;
msl_chipinit(zm);
zm->zm_busy = 0;
splx(s);
}
/*
* Set the chip up for MIDI operation.
*/
msl_chipinit(zm)
register struct zsmidi *zm;
{
register struct zscom *zs = zm->zm_common;
register int x, speed;
char c;
/* Disable receiver */
ZWRITE(3, 0);
zs->zs_addr->zscc_control = ZSWR0_RESET_STATUS;
ZSDELAY(2);
zs->zs_addr->zscc_control = ZSWR0_RESET_ERRORS;
ZSDELAY(2);
/*
* Drain on-chip FIFO.
*/
c = zs->zs_addr->zscc_data;
ZSDELAY(2);
c = zs->zs_addr->zscc_data;
ZSDELAY(2);
c = zs->zs_addr->zscc_data;
ZSDELAY(2);
/*
* Set interrupt enable bits.
*/
x = ZSWR1_RIE|ZSWR1_SIE;
if (zm->zm_fc != ZMFC_SW)
x |= ZSWR1_TIE;
ZWRITE(1, x);
/*
* 2 stop bits.
*/
ZWRITE(4, ZSWR4_X16_CLK | ZSWR4_2_STOP);
/*
* Enable the receiver; 8 data bits.
* If transmitter interrupts will be used, hardware flow
* control *must* be on.
*/
x = ZSWR3_RX_ENABLE|ZSWR3_RX_8;
if (zm->zm_fc == ZMFC_HW)
x |= ZSWR3_AUTO_CD_CTS;
ZWRITE(3, x);
/*
* XXX RTS and DTR must remain in opposite state in order for
* the MIDI interface to extract power from the port.
*/
ZWRITE(5, ZSWR5_DTR | ZSWR5_RTS | ZSWR5_TX_ENABLE | ZSWR5_TX_8);
ZWRITE(11, ZSWR11_TXCLK_BAUD + ZSWR11_RXCLK_BAUD);
/*
* Set the line speed.
*/
ZWRITE(14, ZSWR14_BAUD_FROM_PCLK);
speed = zm->zm_speed;
ZWRITE(12, speed);
ZWRITE(13, speed >> 8);
ZWRITE(14, ZSWR14_BAUD_ENA + ZSWR14_BAUD_FROM_PCLK);
/*
* If doing software flow control, enable interrupts on
* cts transitions.
*/
if (zm->zm_fc == ZMFC_SW)
ZWRITE(15, ZSR15_CTS);
}
static int
msl_out(zm)
register struct zsmidi *zm;
{
register struct mdq *q, *n;
register int c;
again:
q = zm->zm_qh;
if (q == 0)
return 0;
c = *q->mq_cp++;
if (--q->mq_len <= 0) {
n = q->mq_next;
if (n == 0)
zm->zm_qt = 0;
zm->zm_qh = n;
mdqfree(q);
}
if (c & 0x80) {
/*
* This is a status byte. If the running status
* won't change, go get another byte.
*/
if (c == zm->zm_status)
goto again;
/*
* Record the new status. Real time commands (f8-ff)
* don't affect the state so ignore them. If it's a system
* message (f1-f7) reset the status.
*/
if (c < 0xf8)
zm->zm_status = (c < 0xf0) ? c : 0;
}
zm->zm_common->zs_addr->zscc_data = c;
ZSDELAY(2);
return 1;
}
/*
* Start output on the transmitter if it's ready.
* Must be called at splmidi.
*/
void
msl_output(arg, head, tail)
void *arg;
register struct mdq *head;
register struct mdq *tail;
{
register struct zsmidi *zm = (struct zsmidi *)arg;
if (zm->zm_qt == 0) {
zm->zm_qh = head;
zm->zm_qt = tail;
} else {
zm->zm_qt->mq_next = head;
zm->zm_qt = tail;
}
tail->mq_next = 0;
if (zm->zm_busy == 0)
zm->zm_busy = msl_out(zm);
}
/*
* Transmitter interrupt service routine.
*/
static int
zsmidi_txint(zs)
register struct zscom *zs;
{
register struct zsmidi *zm = (struct zsmidi *)zs->zs_priv;
register int s;
s = splmidi();
zm->zm_busy = msl_out(zm);
if (!zm->zm_busy && zm->zm_sleep)
wakeup((caddr_t)zm);
zs->zs_addr->zscc_control = ZSWR0_RESET_TXINT;
splx(s);
}
/*
* External/Status interrupt.
*
*/
static int
zsmidi_xsint(zs)
register struct zscom *zs;
{
register struct zsmidi *zm = (struct zsmidi *)zs->zs_priv;
register struct zscc_device *zsaddr = zs->zs_addr;
register int s;
if (zm->zm_fc != ZMFC_SW) {
zsaddr->zscc_control = ZSWR0_RESET_STATUS;
return;
}
/*
* If hardware flow control won't work (because the SLC has no
* modem control lines on port B), we handle output here.
* Because of the mismatch in baud rates, we must use flow control.
* We wait for the other side to say that they're ready (CTS).
*/
s = zsaddr->zscc_control;
ZSDELAY(2);
if (s & ZSRR0_CTS) {
if ((s & ZSRR0_TX_READY) == 0) {
/*
* If we're doing software flow control, but the
* transmission isn't as fast as the external
* device, we lose and shouldn't be using this
* mode. Just drop the byte.
*/
++zm->zm_txdrops;
} else {
s = splmidi();
zm->zm_busy = msl_out(zm);
splx(s);
}
}
zsaddr->zscc_control = ZSWR0_RESET_STATUS;
}
/*
* Receiver interrupt. Pass character up to higher level.
*/
static int
zsmidi_rxint(zs)
register struct zscom *zs;
{
register int s = splmidi();
bmdinput(((struct zsmidi *)zs->zs_priv)->zm_private,
zs->zs_addr->zscc_data);
splx(s);
}
/*
* Special receive condition interrupt handler.
*/
static int
zsmidi_srint(zs)
register struct zscom *zs;
{
register struct zsmidi *zm = (struct zsmidi *)zs->zs_priv;
register struct zscc_device *zsaddr = zs->zs_addr;
register short s1;
register u_char c;
s1 = ZREAD(1);
ZSDELAY(2);
/*
* Drain bogus character.
*/
c = zsaddr->zscc_data;
ZSDELAY(2);
if (s1 & ZSRR1_PE)
/*
* Log parity error.
*/
++zm->zm_pe;
zsaddr->zscc_control = ZSWR0_RESET_ERRORS;
ZSDELAY(2);
if (s1 & ZSRR1_DO)
/*
* Log hardware overrun.
*/
++zm->zm_overrun;
}
#endif