home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ST-Computer Leser-CD 2000 January
/
LCD_01_2000.iso
/
pd
/
308
/
uart_040
/
pc16550.c
< prev
next >
Wrap
C/C++ Source or Header
|
1999-08-22
|
50KB
|
2,568 lines
/*
* Filename:
* Version:
* Author: Frank Naumann
* Started: 1999-07-28
* Last Updated: 1999-08-06
* Target O/S: TOS/MiNT
* Description:
*
* Note: Please send suggestions, patches or bug reports to me
* or the MiNT mailing list <mint@fishpool.com>.
*
* Copying: Copyright 1999 Frank Naumann <fnaumann@cs.uni-magdeburg.de>
* Portions copyright 1998, 1999 Rainer Mannigel, Michael Schwingen.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*
* changes since last version:
*
* 1999-08-21: (v0.40)
*
* - new: added MiNT version and Milan check
* - new: added tty tread & twrite functions, ttys work now
* - new: added automatic tty install (init, open)
* - new: check & evaluate file sharing modes (open, close)
* - new: wake up sleeping process on close
* - fix: wrong return value in read & write for N_DELAY
*
* 1999-08-14: (v0.30b)
*
* - new: optimized read data interrupt routine
* - new: return EWOULDBLOCK in read & write if N_DELAY is set
*
* 1999-08-09: (v0.20b)
*
* - inital revision
*
*
* known bugs:
*
*
* todo:
*
*
*
* UART BIOS and XBIOS Routinen for Milan.
* Rainer Mannigel, Michael Schwingen
*
* We now support lots (currently 10) of UARTs, and interrupt sharing
* between them. We install one interrupt handler for each physical
* interrupt line that belongs to one or more UARTs, and that gets the
* pointer to the first UART's iovar in its corresponding intr_iovar. The
* iovars are chained together by the next field, so the interrupt handler
* services one UART at a time as long as there are events left to be
* handled and then goes on to the next UART.
*
* Due to the braindamaged design using Bconmap() to access more than one
* serial port, we do not get a useable device number in the BIOS calls
* (Bconin/out/stat/ostat), which means that we have to provide one entry
* point for each of these routines (and for Rsconf, which gets no device
* numbger at all) for every port we want to handle - this is why there is a
* statical limit - these routines are in stubs.s
*/
/* FreeMiNT header */
# include <atarierr.h>
# include <default.h>
# include <dcntl.h>
# include <file.h>
# include <fstring.h>
# include <osbind.h>
# include "kernel.h"
# include "rsvf.h"
# include "pc16550.h"
/*
* version
*/
# define VER_MAJOR 0
# define VER_MINOR 40
# define VER_STATUS
/*
* debugging stuff
*/
# if 0
# define DEV_DEBUG 1
# endif
# if 0
# define INT_DEBUG 1
# endif
/*
* default settings
*/
# define RSVF_BASENAME "serial"
# define RSVF_OFFSETT 1
# define TTY_BASENAME "tty"
# define TTY_OFFSETT 'c'
# define IOBUFSIZE 4096
# define MAX_PORTS 10
# define MAX_INTS 5 /* 2 onboard + 3 ISA cards */
/*
* messages
*/
# define MSG_VERSION str (VER_MAJOR) "." str (VER_MINOR) str (VER_STATUS)
# define MSG_BUILDDATE __DATE__
# define MSG_BOOT \
"\033p Milan UART serial driver version " MSG_VERSION " \033q\r\n"
# define MSG_GREET \
"╜ 1998, 1999 by Rainer Mannigel, Michael Schwingen.\r\n" \
"╜ " MSG_BUILDDATE " by Frank Naumann <fnaumann@cs.uni-magdeburg.de>.\r\n\r\n"
# define MSG_MINT \
"\033pMiNT to old!\033q\r\n"
# define MSG_MILAN \
"\033pThis driver require a Milan!\033q\r\n"
# define MSG_FAILURE \
"\7\r\nSorry, driver NOT installed - initialization failed!\r\n\r\n"
/****************************************************************************/
/* BEGIN kernel interface */
struct kerinfo *kernel;
/* END kernel interface */
/****************************************************************************/
/****************************************************************************/
/* BEGIN definition part */
typedef struct iorec IOREC;
struct iorec
{
uchar *buffer; /* buffer location pointer */
ushort size; /* maximum size of this buffer */
volatile ushort head; /* offset to next byte to be taken
* from this buffer */
volatile ushort tail; /* offset to next location available
* to insert a new byte */
ushort low_water; /* amount of space in buffer before a "xon"
* may be sent to restore normal use of
* buffer */
ushort high_water; /* amount of space used in buffer that
* trigger's the sending of a "xoff" signal
* to the host */
ushort res; /* alignment */
};
typedef struct iovar IOVAR;
struct iovar
{
UART *regs; /* UART register base address */
IOVAR *next; /* for Interrupt-Sharing */
IOREC input; /* input buffer */
IOREC output; /* output buffer */
ushort rxoff; /* flag: receiver stopped (buffer full) */
ushort txoff; /* flag: transmitter stopped */
ushort hsk_mode; /* handshake mode */
# define SHAKE_XON_OFF 0x1
# define SHAKE_RTS_CTS 0x2
uchar sendnow; /* one byte of control data, bypasses buffer */
uchar res1; /* alignment */
long baudrate; /* current baud rate value */
long baudbase; /* crystal frequency / 16 */
ushort intr; /* intr number */
ushort lockpid; /* */
FILEPTR *open; /* */
TTY tty; /* */
};
# define XON 0x11
# define XOFF 0x13
# if 0
void int_autoprobe_start(void);
int int_autoprobe_end(void);
void set_int_levelmode(uchar int_nr);
# endif
/*
* inline assembler primitives
*/
FASTFN ushort spl7 (void);
FASTFN void spl (ushort old_sr);
FASTFN void rts_on (UART *regs);
FASTFN void rts_off (UART *regs);
FASTFN void dtr_on (UART *regs);
FASTFN void dtr_off (UART *regs);
FASTFN void brk_on (UART *regs);
FASTFN void brk_off (UART *regs);
FASTFN void txint_on (UART *regs);
FASTFN void txint_off (UART *regs);
/*
* initialization
*/
FASTFN int detect_uart (UART *regs, ulong *baudbase);
FASTFN int init_uart (IOVAR **iovar, ushort base, int intr, long baudbase);
FASTFN void init_pc16550 (void);
DEVDRV * _cdecl init (struct kerinfo *k);
/*
* buffer manipulation
*/
FASTFN int iorec_empty (IOREC *iorec);
FASTFN int iorec_full (IOREC *iorec);
FASTFN uchar iorec_get (IOREC *iorec);
FASTFN int iorec_put (IOREC *iorec, uchar data);
FASTFN long iorec_used (IOREC *iorec);
FASTFN long iorec_free (IOREC *iorec);
/*
* start/stop primitives
*/
static void stop_receiver (IOVAR *iovar, UART *regs);
static void start_receiver (IOVAR *iovar, UART *regs);
FASTFN void stop_transmitter (IOVAR *iovar);
FASTFN void start_transmitter (IOVAR *iovar);
/*
* interrupt handling
*/
FASTFN void pc16550_read_x (IOVAR *iovar, UART *regs);
FASTFN void pc16550_read_o (IOVAR *iovar, UART *regs);
FASTFN void pc16550_read (IOVAR *iovar, UART *regs);
FASTFN void pc16550_write (IOVAR *iovar, UART *regs);
FASTFN void pc16550_ctsint (IOVAR *iovar, uchar msr);
static void pc16550_int (void);
void pc16550_intx (void);
void pc16550_int0 (void);
void pc16550_int1 (void);
void pc16550_int2 (void);
void pc16550_int3 (void);
void pc16550_int4 (void);
/*
* device driver
*/
static long _cdecl uart_open (FILEPTR *f);
static long _cdecl uart_writeb (FILEPTR *f, const char *buf, long bytes);
static long _cdecl uart_readb (FILEPTR *f, char *buf, long bytes);
static long _cdecl uart_twrite (FILEPTR *f, const char *buf, long bytes);
static long _cdecl uart_tread (FILEPTR *f, char *buf, long bytes);
static long _cdecl uart_lseek (FILEPTR *f, long where, int whence);
static long _cdecl uart_ioctl (FILEPTR *f, int mode, void *buf);
static long _cdecl uart_datime (FILEPTR *f, ushort *timeptr, int rwflag);
static long _cdecl uart_close (FILEPTR *f, int pid);
static long _cdecl uart_select (FILEPTR *f, long proc, int mode);
static void _cdecl uart_unselect (FILEPTR *f, long proc, int mode);
static DEVDRV raw_devtab =
{
uart_open,
uart_writeb, uart_readb, uart_lseek, uart_ioctl, uart_datime,
uart_close,
uart_select, uart_unselect,
};
static DEVDRV tty_devtab =
{
uart_open,
uart_twrite, uart_tread, uart_lseek, uart_ioctl, uart_datime,
uart_close,
uart_select, uart_unselect,
uart_writeb, uart_readb
};
/*
* debugging stuff
*/
# ifdef DEV_DEBUG
# define DEBUG(x) KERNEL_DEBUG x
# define TRACE(x) KERNEL_TRACE x
# define ALERT(x) KERNEL_ALERT x
# else
# define DEBUG(x)
# define TRACE(x)
# define ALERT(x) KERNEL_ALERT x
# endif
# ifdef INT_DEBUG
# define DEBUG_I(x) KERNEL_DEBUG x
# define TRACE_I(x) KERNEL_TRACE x
# define ALERT_I(x) KERNEL_ALERT x
# else
# define DEBUG_I(x)
# define TRACE_I(x)
# define ALERT_I(x) KERNEL_ALERT x
# endif
/* END definition part */
/****************************************************************************/
/****************************************************************************/
/* BEGIN global data definition & access implementation */
/*
* global data structures
*/
static IOVAR *pc16550_iovar [MAX_PORTS * 2];
# define IOVARS(nr) (pc16550_iovar [nr])
# define IOVAR_TTY_OFFSET (MAX_PORTS)
# define IOVAR_MAX (MAX_PORTS * 2 - 1)
/*
* interrupt data structures
*/
/* ptr to first UART struct for each interrupt handler
*/
static IOVAR *intr_iovar [MAX_INTS];
/* ptr to interrupt handler routine, used for Setexc
*/
static void (*intr_handler [MAX_INTS])(void) =
{
pc16550_int0,
pc16550_int1,
pc16550_int2,
pc16550_int3,
pc16550_int4
};
/* END global data & access implementation */
/****************************************************************************/
/****************************************************************************/
/* BEGIN inline assembler primitives */
FASTFN ushort
spl7 (void)
{
ushort old_sr;
__asm__ volatile
(
"move.w %%sr,%0;"
"ori #0x0700,%%sr"
: "=d" (old_sr) /* output register */
: /* input registers */
: "cc" /* clobbered */
);
return old_sr;
}
FASTFN void
spl (ushort old_sr)
{
__asm__ volatile
(
"move.w %0,%%sr"
: /* output register */
: "d" (old_sr) /* input registers */
: "cc" /* clobbered */
);
}
FASTFN void
rts_on (UART *regs)
{
# if 0
ushort sr = spl7 ();
regs->mcr |= RTSO;
spl (sr);
# else
asm volatile
(
"bset.b #1,4(%0)" /* set RTSO in MCR */
:
: "a" (regs)
: "cc"
);
# endif
DEBUG_I (("PC16550 RTS on"));
}
FASTFN void
rts_off (UART *regs)
{
# if 0
ushort sr = spl7 ();
regs->mcr &= ~RTSO;
spl (sr);
# else
asm volatile
(
"bclr.b #1,4(%0)" /* delete RTSO in MCR */
:
: "a" (regs)
: "cc"
);
# endif
DEBUG_I (("PC16550 RTS off"));
}
FASTFN void
dtr_on (UART *regs)
{
DEBUG_I (("PC16550 DTR on"));
# if 0
regs->mcr |= DTRO;
# else
asm volatile
(
"bset.b #0,4(%0)" /* set DTRO in MCR */
:
: "a" (regs)
: "cc"
);
# endif
}
FASTFN void
dtr_off (UART *regs)
{
DEBUG_I (("PC16550 DTR off"));
# if 0
regs->mcr |= DTRO;
# else
asm volatile
(
"bclr.b #0,4(%0)" /* delete DTRO in MCR */
:
: "a" (regs)
: "cc"
);
# endif
}
FASTFN void
brk_on (UART *regs)
{
DEBUG_I (("PC16550 BRK on"));
regs->lcr |= SBRK;
}
FASTFN void
brk_off (UART *regs)
{
DEBUG_I (("PC16550 BRK off"));
regs->lcr &= ~SBRK;
}
FASTFN void
txint_on (UART *regs)
{
DEBUG_I (("PC16550 TXINT on"));
# if 0
regs->ier |= TXLDL_IE;
# else
asm volatile
(
"bset.b #1,1(%0)" /* set TXLDL_IE in IER */
:
: "a" (regs)
: "cc"
);
# endif
}
FASTFN void
txint_off (UART *regs)
{
DEBUG_I (("PC16550 TXINT off"));
# if 0
regs->ier &= ~TXLDL_IE;
# else
asm volatile
(
"bclr.b #1,1(%0)" /* delete TXLDL_IE in IER */
:
: "a" (regs)
: "cc"
);
# endif
}
/* END inline assembler primitives */
/****************************************************************************/
/****************************************************************************/
/* BEGIN initialization */
/*
* Autodetect a UART.
*
* This routine first does some sanity checks to be sure that there is a
* UART at the diven address. It then enables the interrupt and toggles the
* IER to generate an interrupt, to autodetect the interrupt line. The
* interrupt driver is disabled after this, which means that multiple UARTs
* using the same interrupt will be detected OK even if the hardware does
* not support sharing the interrupt line once all UARTs are enabled later.
* The last thing is to measure the BRG crystal by transmitting some
* characters (in loopback mode) with disabled FIFOs and measuring the time
* needed by using MFP timer A.
*
* We time sending 18 characters (or 180 bits) at the highest possible speed
* (BRG divide by 1, or 115200 bps at 1.8432MHz). At a MFP clock of
* 2.4576MHz and a :16 prescaler, this gives the following counted values:
*
* Crystal (MHz) count BRG=1 rate
* 1.8432 240 115200
* 3.6864 120 230400
* 7.3728 60 460800
*
* 4.0 111 250000 (for MIDI cards)
* 6.0 74 375000 (for MIDI cards)
*
*/
/* only in one place used -> FASTFN
*/
FASTFN int
detect_uart (UART *regs, ulong *baudbase)
{
ulong rate = 115200;
int intr = 0;
int i;
*baudbase = 0L;
/* read LSR */
(void) regs->lsr;
/* now LSR may not be $FF! */
if (regs->lsr == 0xff)
return 0;
regs->scr = 0x55;
if (regs->scr != 0x55)
return 0;
regs->scr = 0xAA;
if (regs->scr != 0xAA)
return 0;
/* set DLAB */
regs->lcr = 0x83;
/* set baudrate */
regs->dlm = (115200 / rate) >> 8;
regs->dll = (115200 / rate) & 0xFF;
/* 8N1 */
regs->lcr = 0x03;
regs->ier = 0;
/* Reset FIFOs */
regs->fcr = RXFR | TXFR;
/* Disable FIFOs */
regs->fcr = 0;
/* enable loopback */
regs->mcr = LOOP;
/* check loopback */
if ((regs->msr & 0xf0) != 0)
return 0;
/* enable loopback */
regs->mcr = LOOP | 0x0f;
/* check loopback */
if ((regs->msr & 0xf0) != 0xf0)
return 0;
# if 0
int_autoprobe_start ();
regs->mcr = GI_EN;
/* this will create enough INTs */
regs->ier = 0x00;
regs->ier = 0x0f;
regs->ier = 0x00;
regs->ier = 0x0f;
/* short delay */
for (i = 255; i >= 0; i--)
(void) regs->rbr;
regs->ier = 0;
regs->mcr = 0;
intr = int_autoprobe_end ();
# else
if ((UART *) (0x03f8L + 0xc0000000L) == regs)
{
/* builtin port 1 */
intr = 4;
}
else if ((UART *) (0x02f8L + 0xc0000000L) == regs)
{
/* builtin port 2 */
intr = 3;
}
else
{
/* no detection available yet */
intr = 0;
}
# endif
if (intr > 0)
{
# if 0
*baudbase = 115200L;
# else
/* volatile uchar *tcdr = (volatile uchar *) 0xffffc103L + 17 * 4; */
volatile uchar *tadr = (volatile uchar *) 0xffffc103L + 15 * 4;
volatile uchar *tacr = (volatile uchar *) 0xffffc103L + 12 * 4;
ushort sr;
short time;
sr = spl7 ();
/* enable loopback */
regs->mcr = LOOP;
*tacr = 0;
*tadr = 0xff;
while (*tadr != 0xff)
;
for (i = 0; i < 19; i++)
{
/* send 1 char */
regs->thr = 0x00;
/* wait until done */
while ((regs->lsr & TXDE) == 0)
;
if (i == 0)
*tacr = 3;
}
/* stop timer */
*tacr = 0;
time = 255 - *tadr;
/* get Rx data */
while ((regs->lsr & RXDA))
(void) regs->rbr;
/* disable loopback */
regs->mcr = GI_EN;
(void) regs->iir;
(void) regs->msr;
spl (sr);
if (time > 230 && time <= 250) *baudbase = 115200L;
else if (time > 115 && time <= 125) *baudbase = 230400L;
else if (time > 55 && time <= 65) *baudbase = 460800L;
else if (time > 105 && time <= 115) *baudbase = 250000L;
else if (time > 70 && time <= 80) *baudbase = 375000L;
# endif
DEBUG (("detect_clock: t = %d, baudbase = %lu", time, *baudbase));
}
/* Reset FIFOs */
regs->fcr = FIFO_EN | RXFR | TXFR;
regs->fcr = FIFO_EN | RXFTH_8;
if ((regs->iir & 0xc0) != 0xc0)
{
DEBUG (("no 16550A at %lx, INT %i", regs, intr));
return 0;
}
DEBUG (("detected 16550A at %lx, INT %i", regs, intr));
return intr;
}
/* only in one place used -> FASTFN
*/
FASTFN int
init_uart (IOVAR **iovar, ushort base, int intr, long baudbase)
{
char *buffer;
*iovar = kmalloc (sizeof (**iovar));
if (!*iovar)
goto error;
bzero (*iovar, sizeof (**iovar));
buffer = kmalloc (2 * IOBUFSIZE);
if (!buffer)
goto error;
(*iovar)->input.buffer = buffer;
(*iovar)->output.buffer = buffer + IOBUFSIZE;
(*iovar)->input.size = (*iovar)->output.size = IOBUFSIZE;
(*iovar)->input.low_water = (*iovar)->output.low_water = 1 * (IOBUFSIZE / 4);
(*iovar)->input.high_water = (*iovar)->output.high_water = 3 * (IOBUFSIZE / 4);
(*iovar)->regs = (UART *) (0xC0000000L | base);
(*iovar)->intr = intr;
(*iovar)->baudbase = baudbase;
{
UART *regs = (*iovar)->regs;
long div;
regs->ier = 0;
(*iovar)->baudrate = 9600;
div = (*iovar)->baudbase / 9600;
regs->lcr |= BNKSE;
regs->dll = (div ) & 0xFF;
regs->dlm = (div >> 8) & 0xFF;
regs->lcr &= ~BNKSE;
/* Reset FIFOs */
regs->fcr = FIFO_EN | RXFR | TXFR;
/* Enable FIFOs, set Rcv Threshold */
regs->fcr = FIFO_EN | RXFTH_8;
regs->lcr = 3; /* 8bit char, 1 stopbit, no parity */
(*iovar)->rxoff = 0; /* receiver on */
(*iovar)->txoff = 1; /* transmitter off */
(*iovar)->hsk_mode = SHAKE_RTS_CTS; /* RTS/CTS */
rts_on (regs);
dtr_on (regs);
regs->ier = RXHDL_IE | LS_IE | MS_IE;
regs->mcr |= (GI_EN | RTSO);
}
return 1;
error:
if (*iovar)
kfree (*iovar);
ALERT (("uart.xdd: kmalloc(%li, %li) fail, out of memory?", sizeof (**iovar), IOBUFSIZE));
return 0;
}
/* only in one place used -> FASTFN
*/
FASTFN void
init_pc16550 (void)
{
# define NUM_BASES 12
ushort bases[NUM_BASES] =
{
0x03f8L, 0x02f8L, /* on-board SERIAL1 + SERIAL2 */
0x03e8L, 0x02e8L,
0x02a0L, 0x02a8L, 0x02b0, 0x02b8, /* first AST fourport */
0x01a0L, 0x01a8L, 0x01b0, 0x01b8 /* second AST fourport */
};
ulong baudbase[NUM_BASES];
uchar intr[NUM_BASES];
static int did_init;
int i, j;
ushort sr;
int current_iovar = 0;
int intr_num = 0;
if (!did_init)
{
for (i = 0; i < NUM_BASES; i++)
intr[i] = detect_uart ((UART *) (bases[i] + 0xc0000000L), &baudbase[i]);
did_init = 1;
}
DEBUG (("UARTS found:"));
sr = spl7 ();
for (i = 0; i < NUM_BASES; i++)
{
if (intr[i] && baudbase[i] && current_iovar < MAX_PORTS)
{
DEBUG (("base %x, intr %i, baudbase %lu", bases[i], intr[i], baudbase[i]));
if (init_uart (&(IOVARS (current_iovar)), bases[i], intr[i], baudbase[i]))
current_iovar++;
}
}
/* install interrupt handlers
* chain iovars into a linked list per shared interrupt
*/
for (i = 0; i < current_iovar; i++)
{
int chain_int = 0;
DEBUG (("installing INT for UART %d:",i));
for (j = 0; j < i; j++)
if (IOVARS (i)->intr == IOVARS (j)->intr)
chain_int = j;
if (chain_int)
{
DEBUG (("chaining iovar %d to iovar %d", i, chain_int));
IOVARS (chain_int)->next = IOVARS (i);
/* set_int_levelmode (IOVARS (i)->intr); */
}
else if (intr_num < MAX_INTS)
{
void *old;
DEBUG (("allocating new int handler %d to iovar %d, INT %d", intr_num, i, IOVARS (i)->intr));
intr_iovar[intr_num] = IOVARS (i);
old = Setexc (80 + IOVARS (i)->intr, intr_handler[intr_num]);
DEBUG (("old int handler = %lx", old));
intr_num++;
}
else
DEBUG (("ERROR: no free int handler for UART using int %d", IOVARS (i)->intr));
}
spl (sr);
}
DEVDRV * _cdecl
init (struct kerinfo *k)
{
RSVF *rsvfptr;
RSVF *rsvf;
char *ptr;
long mch;
int initialized;
int i;
struct dev_descr raw_dev_descriptor =
{
&raw_devtab,
0, /* dinfo -> fc.aux */
0, /* flags */
NULL, /* struct tty * */
0, /* drvsize */
S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH,
{ 0, 0 }
};
struct dev_descr tty_dev_descriptor =
{
&tty_devtab,
0, /* dinfo -> fc.aux */
O_TTY, /* flags */
NULL, /* struct tty * */
44, /* drvsize */
S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH,
{ 0, 0 }
};
kernel = k;
c_conws (MSG_BOOT);
c_conws (MSG_GREET);
DEBUG (("%s: enter init", __FILE__));
if ((MINT_MAJOR == 0)
|| ((MINT_MAJOR == 1) && (MINT_MINOR < 15)))
{
c_conws (MSG_MINT);
goto failure;
}
# define SSYS_GETCOOKIE 8
# define COOKIE__MCH 0x5f4d4348L
# define MILAN_C 0x00040000L
if ((s_system (SSYS_GETCOOKIE, COOKIE__MCH, &mch) != 0)
|| (mch != MILAN_C))
{
c_conws (MSG_MILAN);
goto failure;
}
/* temporary until MiNT will do this */
# define RSVF_BUF 1024L
rsvf = rsvfptr = (RSVF *) m_xalloc (RSVF_BUF, 0x40);
if (!rsvf)
{
DEBUG (("%s: m_xalloc fail!", __FILE__));
goto failure;
}
bzero (rsvf, RSVF_BUF);
ptr = (char *) rsvf;
ptr += 512;
init_pc16550 ();
initialized = 1;
for (i = 0; i < MAX_PORTS && IOVARS (i); i++)
{
char name [64];
ksprintf (name, "u:\\dev\\%s%i", RSVF_BASENAME, i + RSVF_OFFSETT);
raw_dev_descriptor.dinfo = i;
raw_dev_descriptor.tty = &(IOVARS (i)->tty);
if (d_cntl (DEV_INSTALL, name, &raw_dev_descriptor) >= 0)
{
DEBUG (("%s: %s installed", __FILE__, name));
rsvfptr->data = ptr;
rsvfptr->type = RSVF_PORT | RSVF_GEMDOS | RSVF_BIOS;
rsvfptr->bdev = 7 + i;
ksprintf (rsvfptr->data, "%s%i", RSVF_BASENAME, i + RSVF_OFFSETT);
DEBUG (("uart: installed %s on %i", rsvfptr->data, 7 + i));
ptr += ((strlen (rsvfptr->data) + 1) + 3) & ~3;
rsvfptr++;
}
ksprintf (name, "u:\\dev\\%s%c", TTY_BASENAME, (char) (i + TTY_OFFSETT));
tty_dev_descriptor.dinfo = i + IOVAR_TTY_OFFSET;
tty_dev_descriptor.tty = &(IOVARS (i)->tty);
if (d_cntl (DEV_INSTALL, name, &tty_dev_descriptor) >= 0)
{
DEBUG (("%s: %s installed", __FILE__, name));
IOVARS (i + IOVAR_TTY_OFFSET) = IOVARS (i);
}
}
if (initialized)
{
# define COOKIE_RSVF 0x52535646L
# define SSYS_SETCOOKIE 9
s_system (SSYS_SETCOOKIE, COOKIE_RSVF, rsvf);
return (DEVDRV *) 1;
}
failure:
# if 0
if (rsvf)
m_free (rsvf);
# endif
c_conws (MSG_FAILURE);
return NULL;
}
/* END initialization */
/****************************************************************************/
/****************************************************************************/
/* BEGIN buffer manipulation */
/* helper function */
FASTFN ushort
inc_ptr (ushort ptr, ushort size)
{
if (++ptr >= size)
ptr = 0;
return ptr;
}
FASTFN int
iorec_empty (IOREC *iorec)
{
return (iorec->head == iorec->tail);
}
FASTFN int
iorec_full (IOREC *iorec)
{
return (iorec->head == inc_ptr (iorec->tail, iorec->size));
}
FASTFN uchar
iorec_get (IOREC *iorec)
{
register ushort i = inc_ptr (iorec->head, iorec->size);
register uchar data;
data = iorec->buffer[i];
iorec->head = i;
return data;
}
FASTFN int
iorec_put (IOREC *iorec, uchar data)
{
register ushort i = inc_ptr (iorec->tail, iorec->size);
if (i == iorec->head)
{
/* buffer full */
return 0;
}
iorec->buffer[i] = data;
iorec->tail = i;
return 1;
}
FASTFN long
iorec_used (IOREC *iorec)
{
register long tmp;
tmp = iorec->tail;
tmp -= iorec->head;
if (tmp < 0)
tmp += iorec->size;
return tmp;
}
FASTFN long
iorec_free (IOREC *iorec)
{
register long tmp;
tmp = iorec->head;
tmp -= iorec->tail;
if (tmp <= 0)
tmp += iorec->size;
return tmp;
}
/* END buffer manipulation */
/****************************************************************************/
/****************************************************************************/
/* BEGIN start/stop primitives */
static void
stop_receiver (IOVAR *iovar, UART *regs)
{
if (!iovar->rxoff)
{
/* set stop flag */
iovar->rxoff = 1;
DEBUG_I (("uart: stop receiver"));
if (iovar->hsk_mode & SHAKE_XON_OFF)
{
/* send XOFF */
iovar->sendnow = XOFF;
/* enable interrupt */
txint_on (regs);
}
if (iovar->hsk_mode & SHAKE_RTS_CTS)
{
/* set rts off */
rts_off (regs);
}
}
}
static void
start_receiver (IOVAR *iovar, UART *regs)
{
if (iovar->rxoff)
{
/* clear stop flag */
iovar->rxoff = 0;
DEBUG_I (("uart: start receiver"));
if (iovar->hsk_mode & SHAKE_RTS_CTS)
{
/* set rts on */
rts_on (regs);
}
if (iovar->hsk_mode & SHAKE_XON_OFF)
{
/* send XON */
iovar->sendnow = XON;
/* enable interrupt */
txint_on (regs);
}
}
}
FASTFN void
stop_transmitter (IOVAR *iovar)
{
/* set stop flag */
iovar->txoff = 1;
DEBUG_I (("uart: transmitter stoped"));
}
FASTFN void
start_transmitter (IOVAR *iovar)
{
DEBUG_I (("uart: transmitter started"));
/* clear stop flag */
iovar->txoff = 0;
/* enable interrupt */
txint_on (iovar->regs);
}
/* END start/stop primitives */
/****************************************************************************/
/****************************************************************************/
/* BEGIN interrupt handling */
/*
* pc16550_read(): uart receive buffer full.
* Holt Zeichen vom UART und schreibt es in den Puffer.
* XON/XOFF- oder RTS/CTS-Mode wird beachtet.
*
* only called from pc16550_int
*/
FASTFN void
pc16550_read_x (IOVAR *iovar, UART *regs)
{
while (regs->lsr & RXDA)
{
register uchar data = regs->rbr;
if (data == XOFF)
{
/* receiver can't accept more data */
stop_transmitter (iovar);
}
else if (data == XON)
{
/* receiver ready now */
start_transmitter (iovar);
}
else
{
DEBUG_I (("rcv: put data %d in buffer", data));
if (!iorec_put (&iovar->input, data))
{
DEBUG_I (("rcvint: buffer full!"));
}
}
}
}
FASTFN void
pc16550_read_o (IOVAR *iovar, UART *regs)
{
while (regs->lsr & RXDA)
{
register uchar data = regs->rbr;
DEBUG_I (("rcv: put data %d in buffer", data));
if (!iorec_put (&iovar->input, data))
{
DEBUG_I (("rcvint: buffer full!"));
}
}
}
FASTFN void
pc16550_read (IOVAR *iovar, UART *regs)
{
if (iovar->hsk_mode & SHAKE_XON_OFF) pc16550_read_x (iovar, regs);
else pc16550_read_o (iovar, regs);
if (iovar->hsk_mode == 0 || iovar->rxoff)
{
/* no handshake or already off */
return;
}
/* in handshake mode test the free space
*/
if (iorec_used (&iovar->input) > iovar->input.high_water)
stop_receiver (iovar, regs);
}
/*
* pc16550_write():
*
* only called from pc16550_int
*/
FASTFN void
pc16550_write (IOVAR *iovar, UART *regs)
{
register int count = 16;
if (iovar->sendnow)
{
/* control byte
*/
DEBUG_I (("pc16550_write: send control %i", iovar->sendnow));
regs->thr = iovar->sendnow;
iovar->sendnow = 0;
count--;
}
/* real data
*/
/* is somebody ready to receive data? */
if (iovar->hsk_mode && iovar->txoff)
{
DEBUG_I (("pc16550_write: sender disabled - INT turned off, ier=$%x", regs->ier));
/* disable interrupt */
txint_off (regs);
}
else
{
while (count--)
{
if (iorec_empty (&iovar->output))
{
DEBUG_I (("pc16550_write: buffer empty - INT turned off, ier=$%x", regs->ier));
/* disable interrupt */
txint_off (regs);
break;
}
/* send character from buffer */
regs->thr = iorec_get (&iovar->output);
}
}
}
/*
* pc16550_ctsint(): CTS-Interruptroutine.
*
* only called from pc16550_int
*/
FASTFN void
pc16550_ctsint (IOVAR *iovar, uchar msr)
{
if (iovar->hsk_mode & SHAKE_RTS_CTS)
{
DEBUG_I (("ctsint: (RTS/CTS active)"));
if (msr & CTSI)
/* CTS is on */
start_transmitter (iovar);
else
/* CTS is off */
stop_transmitter (iovar);
}
}
/*
* Der eigentliche Interrupthandler. Wird vom Assemblerteil aufgerufen.
* HACK: Der IOVAR-Zeiger wird in A0 uebergeben!
*/
static void
pc16550_int (void)
{
IOVAR *iovar;
UART *regs;
ushort sr;
uchar event;
asm volatile
(
"move.l %%a0,%0"
: "=da" (iovar) /* output register */
: /* input registers */
: "cc" /* clobbered */
);
next_uart:
regs = iovar->regs;
DEBUG_I (("pc16550_int: handling UART at %lx", regs));
sr = spl7 ();
while (!((event = (regs->iir & 0x0f)) & 1))
{
/* Int pending */
DEBUG_I (("pc16550_int: event %d ", event));
switch (event)
{
case 6: /* Highest priority */
{
/* Parity error, framing error, data overrun
* or break event
*/
if (regs->lsr & OE)
pc16550_read (iovar, regs);
else
(void) regs->rbr;
break;
}
case 4:
{
/* Receive Buffer Register (RBR) full, or
* reception FIFO level equal to or above
* treshold
*/
}
case 12:
{
/* At least one character is in the reception
* FIFO, and no character has been input to or
* read from the reception FIFO for four
* character times
*/
pc16550_read (iovar, regs);
break;
}
case 2:
{
/* Transmitter Data Register Empty
*/
pc16550_write (iovar, regs);
break;
}
case 0:
{
/* Any transition on /CTS, /DSR or /DCD or a low to
* high transition on /RI
*/
register uchar msr = regs->msr;
if (msr & DCTS)
pc16550_ctsint (iovar, msr);
/* (msr & DDSR)
; */
/* (msr & TERI)
; */
/* (msr & DDCD)
; */
DEBUG_I (("pc16550_int: E0: %d", msr));
break;
}
default:
{
/* unknown event
*/
(void) regs->lsr;
(void) regs->msr;
DEBUG_I (("pc16550_int: unknown event, ignored"));
break;
}
}
}
spl (sr);
iovar = iovar->next;
if (iovar)
{
DEBUG_I (("pc16550_int: continuing on next IOVAR!"));
goto next_uart;
}
DEBUG_I (("PC16550_int: exit"));
}
/* Interrupt-Routinen fuer PC16550 - rufen die eigentlichen C-Routinen auf
*/
void
pc16550_intx (void)
{
(void) pc16550_int; /* tell the compiler that we need it */
asm volatile
(
"_pc16550_int0:
movem.l %%a0-%%a2/%%d0-%%d2,-(%%sp)
move.l _intr_iovar,%%a0
bsr _pc16550_int
movem.l (%%sp)+,%%a0-%%a2/%%d0-%%d2
rte"
: /* output register */
: /* input registers */
/* clobbered */
);
asm volatile
(
"_pc16550_int1:
movem.l %%a0-%%a2/%%d0-%%d2,-(%%sp)
move.l _intr_iovar+4,%%a0
bsr _pc16550_int
movem.l (%%sp)+,%%a0-%%a2/%%d0-%%d2
rte"
: /* output register */
: /* input registers */
/* clobbered */
);
asm volatile
(
"_pc16550_int2:
movem.l %%a0-%%a2/%%d0-%%d2,-(%%sp)
move.l _intr_iovar+8,%%a0
bsr _pc16550_int
movem.l (%%sp)+,%%a0-%%a2/%%d0-%%d2
rte"
: /* output register */
: /* input registers */
/* clobbered */
);
asm volatile
(
"_pc16550_int3:
movem.l %%a0-%%a2/%%d0-%%d2,-(%%sp)
move.l _intr_iovar+12,%%a0
bsr _pc16550_int
movem.l (%%sp)+,%%a0-%%a2/%%d0-%%d2
rte"
: /* output register */
: /* input registers */
/* clobbered */
);
asm volatile
(
"_pc16550_int4:
movem.l %%a0-%%a2/%%d0-%%d2,-(%%sp)
move.l _intr_iovar+16,%%a0
bsr _pc16550_int
movem.l (%%sp)+,%%a0-%%a2/%%d0-%%d2
rte"
: /* output register */
: /* input registers */
/* clobbered */
);
}
/* END interrupt handling */
/****************************************************************************/
/****************************************************************************/
/* BEGIN */
static void _cdecl
check_select (void)
{
int flag = 0;
int i;
for (i = 0; IOVARS (i); i++)
{
IOVAR *iovar = IOVARS (i);
if (iovar->tty.rsel)
{
if (!iorec_empty (&iovar->input))
wakeselect (iovar->tty.rsel);
else
flag = 1;
}
if (iovar->tty.wsel)
{
if (iorec_free (&iovar->output))
wakeselect (iovar->tty.wsel);
else
flag = 1;
}
}
if (flag)
addroottimeout (1000L, check_select, 0);
}
/* END */
/****************************************************************************/
/****************************************************************************/
/* BEGIN device driver routines */
static long _cdecl
uart_open (FILEPTR *f)
{
ushort dev = f->fc.aux;
IOVAR *iovar;
DEBUG (("uart_open [%i]: enter (%lx)", f->fc.aux, f->flags));
if (dev > IOVAR_MAX)
return EACCDN;
iovar = IOVARS (dev);
if (iovar->open && denyshare (iovar->open, f))
{
DEBUG (("uart_open: file sharing denied"));
return EACCDN;
}
f->pos = 0;
f->next = iovar->open;
iovar->open = f;
if (dev >= IOVAR_TTY_OFFSET)
f->flags |= O_TTY;
return E_OK;
}
static long _cdecl
uart_writeb (FILEPTR *f, const char *buf, long bytes)
{
IOVAR *iovar = IOVARS (f->fc.aux);
IOREC *iorec = &iovar->output;
long done = 0;
DEBUG (("uart_writeb [%i]: enter (%lx, %ld)", f->fc.aux, buf, bytes));
for (;;)
{
/* copy as much as possible */
while ((bytes > 0) && iorec_put (iorec, *buf))
{
DEBUG (("%x - %c", (int) *buf, *buf));
buf++; done++;
bytes--;
}
/* start transfer - interrupt controlled */
start_transmitter (iovar);
if (f->flags & O_NDELAY)
{
if (!done)
done = EWOULDBLOCK;
break;
}
if (!bytes)
break;
/* sleep until there is enough room in the buffer
* to continue
*/
while (iorec_used (iorec) > iorec->high_water)
sleep (READY_Q, 0L);
}
if (done > 0)
{
struct bios_file *b = (struct bios_file *) f->fc.index;
b->xattr.atime = timestamp;
b->xattr.adate = datestamp;
}
DEBUG (("uart_writeb: leave (%ld)", done));
return done;
}
static long _cdecl
uart_readb (FILEPTR *f, char *buf, long bytes)
{
IOVAR *iovar = IOVARS (f->fc.aux);
IOREC *iorec = &iovar->input;
long done = 0;
DEBUG (("uart_readb [%i]: enter (%lx, %ld)", f->fc.aux, buf, bytes));
for (;;)
{
/* copy as much as possible */
while ((bytes > 0) && !iorec_empty (iorec))
{
*buf = iorec_get (iorec);
DEBUG (("%x - %c", (int) *buf, *buf));
buf++; done++;
bytes--;
}
if (iorec_used (iorec) < iorec->low_water)
/* start receiver */
start_receiver (iovar, iovar->regs);
if (f->flags & O_NDELAY)
{
if (!done)
done = EWOULDBLOCK;
break;
}
if (!bytes)
break;
/* sleep until we received enough data or the buffer
* become to full
*/
{
long i;
i = iorec_used (iorec);
while ((i < bytes) && (i < iorec->high_water))
{
sleep (READY_Q, 0L);
i = iorec_used (iorec);
}
}
}
if (done > 0)
{
struct bios_file *b = (struct bios_file *) f->fc.index;
b->xattr.atime = timestamp;
b->xattr.adate = datestamp;
}
DEBUG (("uart_readb: leave (%ld)", done));
return done;
}
/*
* Note: when a BIOS device is a terminal (i.e. has the O_TTY flag
* set), bios_read and bios_write will only ever be called indirectly, via
* tty_read and tty_write. That's why we can afford to play a bit fast and
* loose with the pointers ("buf" is really going to point to a long) and
* why we know that "bytes" is divisible by 4.
*/
static long _cdecl
uart_twrite (FILEPTR *f, const char *buf, long bytes)
{
IOVAR *iovar = IOVARS (f->fc.aux);
IOREC *iorec = &iovar->output;
long done = 0;
const long *r = (const long *) buf;
DEBUG (("uart_twrite [%i]: enter (%lx, %ld)", f->fc.aux, buf, bytes));
for (;;)
{
/* copy as much as possible */
while ((bytes > 0) && iorec_put (iorec, (char) *r))
{
DEBUG (("%lx - %c", *r, (char) *r));
r++; done += 4;
bytes -= 4;
}
/* start transfer - interrupt controlled */
start_transmitter (iovar);
if (f->flags & O_NDELAY)
{
if (!done)
done = EWOULDBLOCK;
break;
}
if (!bytes)
break;
/* sleep until there is enough room in the buffer
* to continue
*/
while (iorec_used (iorec) > iorec->high_water)
sleep (READY_Q, 0L);
}
if (done > 0)
{
struct bios_file *b = (struct bios_file *) f->fc.index;
b->xattr.atime = timestamp;
b->xattr.adate = datestamp;
}
DEBUG (("uart_twrite: leave (%ld)", done));
return done;
}
static long _cdecl
uart_tread (FILEPTR *f, char *buf, long bytes)
{
IOVAR *iovar = IOVARS (f->fc.aux);
IOREC *iorec = &iovar->input;
long done = 0;
long *r = (long *) buf;
DEBUG (("uart_tread [%i]: enter (%lx, %ld)", f->fc.aux, buf, bytes));
for (;;)
{
/* copy as much as possible */
while ((bytes > 0) && !iorec_empty (iorec))
{
*r = (long) iorec_get (iorec);
DEBUG (("%lx - %c", *r, (char) *r));
r++; done += 4;
bytes -= 4;
}
if (iorec_used (iorec) < iorec->low_water)
/* start receiver */
start_receiver (iovar, iovar->regs);
if (f->flags & O_NDELAY)
{
if (!done)
done = EWOULDBLOCK;
break;
}
if (!bytes)
break;
/* sleep until we received enough data or the buffer
* become to full
*/
{
long i;
i = iorec_used (iorec);
while ((i < bytes) && (i < iorec->high_water))
{
sleep (READY_Q, 0L);
i = iorec_used (iorec);
}
}
}
if (done > 0)
{
struct bios_file *b = (struct bios_file *) f->fc.index;
b->xattr.atime = timestamp;
b->xattr.adate = datestamp;
}
DEBUG (("uart_tread: leave (%ld)", done));
return done;
}
static long _cdecl
uart_lseek (FILEPTR *f, long where, int whence)
{
DEBUG (("uart_lseek [%i]: enter (%ld, %d)", f->fc.aux, where, whence));
/* (terminal) devices are always at position 0 */
return 0;
}
static long _cdecl
uart_ioctl (FILEPTR *f, int mode, void *buf)
{
IOVAR *iovar = IOVARS (f->fc.aux);
long r = E_OK;
DEBUG (("uart_ioctl [%i]: (%x, (%c %i), %lx)", f->fc.aux, mode, (char) (mode >> 8), (mode & 0xff), buf));
switch (mode)
{
case FIONREAD:
{
*(long *) buf = iorec_used (&iovar->input);
break;
}
case FIONWRITE:
{
long bytes;
bytes = iorec_free (&iovar->output);
bytes -= 2;
if (bytes < 0)
bytes = 0;
*(long *) buf = bytes;
break;
}
case FIOEXCEPT: /* anywhere documented? */
{
*(long *) buf = 0;
break;
}
case TIOCFLUSH:
{
int flushtype;
/* HSMODEM put the value itself in arg
* MiNT use arg as a pointer to the value
*
* if opened as terminal we assume MiNT convention
* otherwise HSMODEM convention
*/
if (f->fc.aux >= IOVAR_TTY_OFFSET)
flushtype = *(int *) buf;
else
flushtype = (int) ((long) buf);
if (flushtype <= 0)
{
r = EINVFN;
break;
}
if (flushtype & 1)
{
IOREC *iorec = &(iovar->input);
ushort sr;
sr = spl7 ();
iorec->head = iorec->tail = 0;
spl (sr);
}
if (flushtype & 2)
{
IOREC *iorec = &(iovar->output);
ushort sr;
sr = spl7 ();
iorec->head = iorec->tail = 0;
spl (sr);
}
break;
}
case TIOCSTOP: /* HSMODEM */
{
if (iovar->hsk_mode)
stop_receiver (iovar, iovar->regs);
break;
}
case TIOCSTART: /* HSMODEM */
{
start_receiver (iovar, iovar->regs);
break;
}
case TIOCIBAUD:
case TIOCOBAUD:
{
long speed = *(long *) buf;
DEBUG (("uart_ioctl(TIOCIBAUD) to %li", speed));
if (speed == -1)
{
/* current baudrate */
*(long *) buf = iovar->baudrate;
break;
}
else if (speed == 0)
{
/* clear dtr */
dtr_off (iovar->regs);
break;
}
else
{
/* set baudrate to speed, enable dtr */
static const long table [] =
{
300, 600,
1200, 1800, 2400, 3600, 4800, 9600,
14400, 19200, 28800, 38400, 57600,
115200, 230400, 460800,
0
};
long baudrate;
long div;
div = iovar->baudbase / speed;
if (div < 1)
div = 1;
if (div > 65534)
div = 65534;
baudrate = iovar->baudbase / div;
if ((baudrate != speed) || (iovar->baudbase % div))
{
# if 0
if (baudrate > speed)
{
div++;
baudrate = iovar->baudbase / div;
}
if (baudrate < 9600)
baudrate = 9600;
# else
if (speed > iovar->baudbase)
{
baudrate = iovar->baudbase;
}
else
{
int i;
baudrate = table [0];
for (i = 0; table [i] != 0; i++)
{
if (speed > table [i] && speed <= iovar->baudbase)
baudrate = table [i];
}
}
# endif
*(long *) buf = baudrate;
DEBUG (("uart_ioctl(TIOCIBAUD) error -> %li", baudrate));
r = ERANGE;
break;
}
if (baudrate != iovar->baudrate)
{
UART *regs = iovar->regs;
ushort sr;
uchar old_ier;
sr = spl7 ();
/* save old interrupts */
old_ier = regs->ier;
/* and lock */
regs->ier = 0;
old_ier |= (LS_IE | MS_IE);
/* set new daudrate
*/
iovar->baudrate = baudrate;
div = iovar->baudbase / baudrate;
regs->lcr |= BNKSE;
regs->dll = (div) & 0xFF;
regs->dlm = (div >> 8) & 0xFF;
regs->lcr &= ~BNKSE;
/* Reset FIFOs */
regs->fcr = FIFO_EN | RXFR | TXFR;
/* Enable FIFOs, set Rcv Threshold */
regs->fcr = FIFO_EN | RXFTH_8;
/* freeup new interrupts */
regs->ier = old_ier;
regs->mcr |= (GI_EN | RTSO);
spl (sr);
}
/* always enable dtr */
dtr_on (iovar->regs);
}
break;
}
case TIOCCBRK:
{
brk_off (iovar->regs);
break;
}
case TIOCSBRK:
{
brk_on (iovar->regs);
break;
}
case TIOCGFLAGS:
{
ushort flags = 0;
uchar lcr = iovar->regs->lcr;
switch (lcr & WLS)
{
case 0: flags |= TF_5BIT; break;
case 1: flags |= TF_6BIT; break;
case 2: flags |= TF_7BIT; break;
case 3: flags |= TF_8BIT; break;
}
if (lcr & STB) flags |= TF_2STOP;
else flags |= TF_1STOP;
if (lcr & PEN)
{
if (lcr & EPS) flags |= T_EVENP;
else flags |= T_ODDP;
}
if (iovar->hsk_mode & SHAKE_XON_OFF)
flags |= T_TANDEM;
if (iovar->hsk_mode & SHAKE_RTS_CTS)
flags |= T_RTSCTS;
*(ushort *) buf = flags;
break;
}
case TIOCSFLAGS:
{
ushort flags = *(ushort *) buf;
ushort lcr = iovar->regs->lcr;
lcr &= ~WLS;
switch (flags & TF_CHARBITS)
{
case TF_5BIT: lcr |= 0; break;
case TF_6BIT: lcr |= 1; break;
case TF_7BIT: lcr |= 2; break;
case TF_8BIT: lcr |= 3; break;
default: goto err_erange; break;
}
switch (flags & TF_STOPBITS)
{
case TF_1STOP: lcr &= ~STB; break;
case TF_15STOP: goto err_erange; break;
case TF_2STOP: lcr |= STB; break;
default: goto err_erange; break;
}
if (flags & (T_EVENP | T_ODDP))
{
if ((flags & (T_EVENP | T_ODDP)) == (T_EVENP | T_ODDP))
goto err_erange;
/* enable parity */
lcr |= PEN;
/* set even/odd parity */
if (flags & T_EVENP) lcr |= EPS;
else lcr &= ~EPS;
}
else
{
/* disable parity */
lcr &= ~PEN;
/* even/odd bit ignored in this case */
}
if (flags & T_TANDEM) iovar->hsk_mode |= SHAKE_XON_OFF;
else iovar->hsk_mode &= ~SHAKE_XON_OFF;
if (flags & T_RTSCTS) iovar->hsk_mode |= SHAKE_RTS_CTS;
else iovar->hsk_mode &= ~SHAKE_RTS_CTS;
/* setup in register */
iovar->regs->lcr = lcr;
break;
}
case TIONOTSEND:
case TIOCOUTQ:
{
*(long *) buf = iorec_used (&iovar->output);
break;
}
case TIOCSFLAGSB: /* anywhere documented? */
{
r = EINVFN;
break;
}
case TIOCGVMIN:
{
struct tty *tty = (struct tty *) f->devinfo;
ushort *v = buf;
v [0] = tty->vmin;
v [1] = tty->vtime;
break;
}
case TIOCSVMIN:
{
struct tty *tty = (struct tty *) f->devinfo;
ushort *v = buf;
if (v [0] > iovar->input.size / 2)
v [0] = iovar->input.size / 2;
tty->vmin = v [0];
tty->vtime = v [1];
/* t->vticks = 0; */
break;
}
case TIOCWONLINE:
{
# if 0
struct tty *tty = (struct tty *) f->devinfo;
while (tty->state & TS_BLIND)
sleep (IO_Q, (long) &tty->state);
# else
r = EINVFN;
# endif
break;
}
case TIOCBUFFER: /* HSMODEM special */
{
long *ptr = (long *) buf;
/* input buffer size */
if (*ptr == -1) *ptr = iovar->input.size;
else *ptr = -1;
ptr++;
/* input low watermark */
if (*ptr == -1) *ptr = iovar->input.low_water;
else *ptr = -1;
ptr++;
/* input high watermark */
if (*ptr == -1) *ptr = iovar->input.high_water;
else *ptr = -1;
ptr++;
/* output buffer size */
if (*ptr == -1) *ptr = iovar->output.size;
else *ptr = -1;
break;
}
case TIOCCTLMAP: /* HSMODEM */
{
long *ptr = (long *) buf;
ptr [0] = 0
/* | TIOCMH_LE */
| TIOCMH_DTR
| TIOCMH_RTS
| TIOCMH_CTS
| TIOCMH_CD
| TIOCMH_RI
| TIOCMH_DSR
/* | TIOCMH_LEI */
/* | TIOCMH_TXD */
/* | TIOCMH_RXD */
| TIOCMH_BRK
/* | TIOCMH_TER */
/* | TIOCMH_RER */
| TIOCMH_TBE
| TIOCMH_RBF
;
ptr [1] = 0; /* will be never supported */
ptr [2] = 0; /* will be never supported */
ptr [3] = 0; /* reserved */
ptr [4] = 0; /* reserved */
ptr [5] = 0; /* reserved */
break;
}
case TIOCCTLGET: /* HSMODEM */
{
/* register long mask = *(long *) buf; */
register long val = 0;
register char reg;
/* TIOCMH_LE */
/* mcr */
reg = iovar->regs->mcr;
if (reg & DTRO) val |= TIOCMH_DTR;
if (reg & RTSO) val |= TIOCMH_RTS;
/* msr */
reg = iovar->regs->msr;
if (reg & CTSI) val |= TIOCMH_CTS;
if (reg & RII ) val |= TIOCMH_RI;
if (reg & DCDI) val |= TIOCMH_CD;
if (reg & DSRI) val |= TIOCMH_DSR;
/* TIOCMH_LEI */
/* TIOCMH_TXD */
/* TIOCMH_RXD */
/* lsr */
reg = iovar->regs->lsr;
if (reg & BRK ) val |= TIOCMH_BRK;
/* TIOCMH_TER */
/* TIOCMH_RER */
if (reg & TXDE) val |= TIOCMH_TBE;
if (reg & RXDA) val |= TIOCMH_RBF;
*(long *) buf = val;
break;
}
case TIOCCTLSET: /* HSMODEM */
{
long *arg = (long *) buf;
register long mask = *arg++;
register long val = *arg;
if (mask & (TIOCMH_DTR | TIOCMH_RTS))
{
/* mcr */
register char reg_val = iovar->regs->mcr;
if (mask & TIOCMH_DTR)
{
if (val & TIOCMH_DTR) reg_val |= DTRO;
else reg_val &= ~DTRO;
}
if (mask & TIOCMH_RTS)
{
if (val & TIOCMH_RTS) reg_val |= RTSO;
else reg_val &= ~RTSO;
}
iovar->regs->mcr = reg_val;
}
break;
}
case TIOCERROR: /* HSMODEM */
{
r = ERANGE;
break;
}
case TIOCCDTR:
{
dtr_off (iovar->regs);
break;
}
case TIOCSDTR:
{
dtr_on (iovar->regs);
break;
}
case F_SETLK:
case F_SETLKW:
{
struct flock *lck = (struct flock *) buf;
int cpid = p_getpid ();
while (iovar->lockpid && iovar->lockpid != cpid)
{
if (mode == F_SETLKW && lck->l_type != F_UNLCK)
sleep (IO_Q, (long) iovar);
else
return ELOCKED;
}
if (lck->l_type == F_UNLCK)
{
if (!(f->flags & O_LOCK))
{
DEBUG (("uart_ioctl: wrong file descriptor for UNLCK"));
return ENSLOCK;
}
if (iovar->lockpid != cpid)
return ENSLOCK;
iovar->lockpid = 0;
f->flags &= ~O_LOCK;
/* wake anyone waiting for this lock */
wake (IO_Q, (long) iovar);
}
else
{
iovar->lockpid = cpid;
f->flags |= O_LOCK;
}
break;
}
case F_GETLK:
{
struct flock *lck = (struct flock *) buf;
if (iovar->lockpid)
{
lck->l_type = F_WRLCK;
lck->l_start = lck->l_len = 0;
lck->l_pid = iovar->lockpid;
}
else
{
lck->l_type = F_UNLCK;
}
break;
}
default:
{
/* Fcntl will automatically call tty_ioctl to handle
* terminal calls that we didn't deal with
*/
r = EINVFN;
break;
}
}
goto out;
err_erange:
r = ERANGE;
out:
DEBUG (("uart_ioctl: return %li", r));
return r;
}
static long _cdecl
uart_datime (FILEPTR *f, ushort *timeptr, int rwflag)
{
DEBUG (("uart_datime [%i]: enter (%i)", f->fc.aux, rwflag));
if (rwflag)
return EACCDN;
*timeptr++ = timestamp;
*timeptr = datestamp;
return E_OK;
}
static long _cdecl
uart_close (FILEPTR *f, int pid)
{
IOVAR *iovar = IOVARS (f->fc.aux);
DEBUG (("uart_close [%i]: enter", f->fc.aux));
if (iovar->lockpid == pid)
{
/* wake anyone waiting for this lock */
wake (IO_Q, (long) iovar);
}
if (f->links <= 0)
{
register FILEPTR **temp;
register long flag = 1;
/* remove the FILEPTR from the linked list */
temp = &iovar->open;
while (*temp)
{
if (*temp == f)
{
*temp = f->next;
f->next = NULL;
flag = 0;
break;
}
temp = &(*temp)->next;
}
if (flag)
{
ALERT (("uart_close: remove open FILEPTR fail!", f->fc.aux));
}
}
DEBUG (("uart_close: leave ok"));
return E_OK;
}
static long _cdecl
uart_select (FILEPTR *f, long proc, int mode)
{
IOVAR *iovar = IOVARS (f->fc.aux);
struct tty *tty = (struct tty *) f->devinfo;
DEBUG (("uart_select [%i]: enter (%li, %i, %lx)", f->fc.aux, proc, mode, tty));
if (mode == O_RDONLY)
{
if (!iorec_empty (&iovar->input))
{
TRACE (("uart_select: data present for device %lx", iovar));
return 1;
}
if (tty)
{
/* avoid collisions with other processes
*/
if (tty->rsel)
/* collision */
return 2;
tty->rsel = proc;
}
return 0;
}
else if (mode == O_WRONLY)
{
if ((!tty || !(tty->state & (TS_BLIND | TS_HOLD))) && !iorec_empty (&iovar->output))
{
TRACE (("uart_select: ready to output on %lx", iovar));
return 1;
}
if (tty)
{
/* avoid collisions with other processes
*/
if (tty->wsel)
/* collision */
return 2;
tty->wsel = proc;
}
return 0;
}
/* default -- we don't know this mode, return 0 */
return 0;
}
static void _cdecl
uart_unselect (FILEPTR *f, long proc, int mode)
{
struct tty *tty = (struct tty *) f->devinfo;
DEBUG (("uart_unselect [%i]: enter (%li, %i, %lx)", f->fc.aux, proc, mode, tty));
if (tty)
{
if (mode == O_RDONLY && tty->rsel == proc)
tty->rsel = 0;
else if (mode == O_WRONLY && tty->wsel == proc)
tty->wsel = 0;
}
}
/* END device driver routines */
/****************************************************************************/