home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mega CD-ROM 1
/
megacd_rom_1.zip
/
megacd_rom_1
/
MAGAZINE
/
DDJMAG
/
DDJ9111.ZIP
/
PORTUNIX.ASC
< prev
next >
Wrap
Text File
|
1991-10-18
|
21KB
|
796 lines
_PORTING UNIX APPLICATIONS TO DOS_
by David N. Glass
[LISTING ONE]
/* Serial support for target communications. Assumes 8250 serial device */
#include <dos.h>
#include <stdio.h>
#include <string.h>
#include <i32.h>
#include <process.h>
#include "ttycntl.h"
#include "gnudos.h"
static char serial_device[20];
static int serial_baud;
static int serial_devtype; /* Device identifier. */
static int serial_iobase; /* I/O base value. */
static int serial_ivec; /* Host interrupt vector. */
static char serial_opened; /* Flag set if file is opened */
static char serial_setup; /* Flag set if setup for port has started */
static char serial_updated; /* Flag set if port values were altered */
static struct dupdef {
int num;
int max;
int *fd;
} serial_dup = { 0, 5, NULL };
static void (*orig_serial_vec)() = 0;
#define IR3 0x0b
#define IR4 0x0c
#define IR5 0x0d
#define IMR_8259 0x21 /* int mask reg */
#define LOOPERMS 33 /* rough loop count per ms */
#define BREAKMS 1000 /* break length in ms */
/*
* Map bauds onto counter values
*/
static struct baudmap {
unsigned long baud;
int code;
} baudmap[] ={
{ 1200, 0x060},
{ 2400, 0x030},
{ 4800, 0x018},
{ 9600, 0x00C},
{ 19200, 6},
{ 38400, 3},
{ 57600, 2},
{ 115200, 1},
{ 166600, -1},
{ 250000, -1},
{ 500000, -1},
{ 750000, -1},
{1500000, -1},
{ 0, 0}
};
/*
* Input queue
*/
#define QBUFSZ (3*1024) /* queue size */
static struct queue {
int q_cnt; /* amount in buffer */
unsigned q_ovfl; /* number of overflows */
char * q_in; /* next free spot */
char * q_out; /* next char out */
char q_buf[QBUFSZ]; /* character buffer */
} siq; /* chars from remote port */
static int get_port(int comwhich);
static void init_8259(unsigned int int_value);
static int is_dupd(int);
static int is_serial(int);
static void sendbreak(void);
static void senq(int);
static int serial_read1(int, char *, int);
static int set_mode( unsigned long, unsigned);
static void sputch(int);
static void uinit_8259(unsigned int int_value);
/* 8250 register addresses */
#define LCNT_REG (serial_iobase + 0)
#define HCNT_REG (serial_iobase + 1)
#define IER_REG (serial_iobase + 1)
#define ISR_REG (serial_iobase + 2)
#define LCR_REG (serial_iobase + 3)
#define MCR_REG (serial_iobase + 4)
#define ST_REG (serial_iobase + 5)
/* 8250 status register bits */
#define GETRDYBIT 0x01
#define PUTRDYBIT 0x20
/* 8250 interrupt register bits */
#define ENTXINT 0x02
#define ENRXINT 0x01
/* 8250 line control register bits */
#define ENCNTREG 0x80
#define SETBREAK 0x40
#define CHLEN1 0x02
#define CHLEN0 0x01
/* 8250 modem control register btis */
#define OUT2 0x08
#define RTS 0x02 /* inverted */
#define DTR 0x01 /* inverted */
/*
* TIMER ROUTINES
*/
#define TKPSEC 17
#define MSECPTK (1000/TKPSEC)
#define TMRVEC 0x1c
static int bad; /* bad pointer, in case of bad timeout pointer */
static int *timeoutp = &bad; /* timeout flag pointer */
static unsigned short timecnt = 0; /* timeout ticks remaining */
static int inited = 0;
static void (*prev_timer_int)() = 0;
static int occurcnt = 0; /* debug, time out count */
/******************************************************************************
* settimeout()
* Set a "background" timer which sets requested flag when alloted
* interval expires.
*
* WARNING: Normal usage would settimeout( > 0 ), process until some event
* occurs OR this flag sets (indicating times up), then settimeout(0)
* in order to turn the timer off.
* This is VERY important, because if the requester flag is for instance,
* a stack variable, and the caller RETURNs before the timeout; then, when
* the timer does go off, it will set some, now defunct location.
******************************************************************************/
static void
settimeout(ms, flagp)
unsigned ms;
int *flagp;
{
int pif;
if (!inited) {
init_timer();
}
pif = change_if(0);
if (ms == 0) {
timeoutp = &bad;
timecnt = 0;
} else {
timeoutp = flagp;
timecnt = (ms + (MSECPTK - 1)) / MSECPTK + 1;
}
change_if(pif);
}
/******************************************************************************
* TICK()
* C interrupt service routine.
******************************************************************************/
#pragma interrupt(TICK)
static void
TICK(void)
{
if (timecnt && !--timecnt) {
*timeoutp = 1;
++occurcnt; /* for debug */
}
_chain_intr(prev_timer_int);
}
/******************************************************************************
* init_timer()
* Initialize our timer support. Return 1 on success, 0 on failure.
******************************************************************************/
static int
init_timer(void)
{
if (inited) {
return 0;
}
prev_timer_int = _dos_getvect(TMRVEC);
_dos_setvect(TMRVEC, TICK);
inited = 1;
return 1;
}
/******************************************************************************
* term_timer()
* Terminate our timer support, releasing any resources which were used.
******************************************************************************/
static int
term_timer(void)
{
if (inited) {
_dos_setvect(TMRVEC, prev_timer_int);
inited = 0;
}
}
static int
xltbaud(baud, baudmap)
unsigned long baud;
register struct baudmap *baudmap;
{
for ( ; baudmap->baud; baudmap++){
if (baudmap->baud == baud){
return baudmap->code;
}
}
return -1;
}
/******************************************************************************
* sputch()
* Put a serial character, wait if not currently ready to send.
******************************************************************************/
static void
sputch(ch)
int ch;
{
while ( !(_inbyte(ST_REG) & PUTRDYBIT) ){
;
}
_outbyte( serial_iobase, ch );
}
/******************************************************************************
* sendbreak()
* Send a break "character" (RS232 low for more than a character period).
******************************************************************************/
static void
sendbreak()
{
unsigned char ch;
int cnt;
ch = _inbyte( LCR_REG );
_outbyte( LCR_REG, ch|SETBREAK );
for ( cnt = BREAKMS*LOOPERMS; cnt; --cnt ){
;
}
_outbyte( LCR_REG, ch );
}
/******************************************************************************
* serial_open()
* Open the serial device passed in as a parameter and prepare it for IO.
* Return -1 on error (unable to initialize requested device).
******************************************************************************/
static int
serial_open()
{
if (strncmp(serial_device, "com", 3) == 0) {
int port = serial_device[3] - '0';
if (serial_iobase == 0){
serial_iobase = get_port(port);
}
if (serial_ivec == 0) {
switch (port) {
case 1: serial_ivec = IR4; break; /* COM1 interrupt */
case 2: serial_ivec = IR3; break; /* COM2 interrupt */
case 3: serial_ivec = IR5; break; /* COM3 interrupt */
}
}
} else if (strcmp(serial_device, "aux") == 0) {
if (serial_iobase == 0){
serial_iobase = 0x3e8;
}
if (serial_ivec == 0){
serial_ivec = IR5;
}
}
if ( (serial_iobase == 0)
|| (serial_ivec == 0)
|| !set_mode(serial_baud,serial_ivec)
|| !init_timer()) {
return -1;
}
return 0;
}
/******************************************************************************
* serial_close()
* Close the serial device, terminating usage in current context.
******************************************************************************/
static void
serial_close()
{
_outbyte( IER_REG, 0); /* disable ints */
_outbyte( MCR_REG, 0);
uinit_8259(serial_ivec);
term_timer();
}
static int
serial_read1(int port, char *bufptr, int want)
{
int cnt;
int pif;
pif = change_if( 0 );
if ((cnt = siq.q_in - siq.q_out) < 0){
cnt = &siq.q_buf[QBUFSZ] - siq.q_out; /* don't wrap */
}
change_if( pif );
if (cnt == 0) {
return 0;
}
if (want < cnt){
cnt = want;
}
memcpy( bufptr, siq.q_out, cnt );
pif = change_if( 0 );
siq.q_cnt -= cnt;
if ((siq.q_out += cnt) >= &siq.q_buf[QBUFSZ]) {
siq.q_out = siq.q_buf;
} else if (siq.q_in == siq.q_out) {
siq.q_in = siq.q_out = siq.q_buf; /* reduce wraps */
}
change_if( pif );
return cnt;
}
/******************************************************************************
* serial_read()
* Attempt to read N bytes from the serial device, waiting up to
* timo (time-out) milliseconds for data to become available. Return:
* 0-N Actual number of bytes read, 0 if none.
* -1 error condition
******************************************************************************/
static int
serial_read(int port, char *buf, int size, unsigned timo)
{
int actual;
int flag;
flag = 0;
settimeout(timo, &flag);
do {
actual = serial_read1(port, (char *)buf, size);
} while ((actual == 0) && !flag);
settimeout(0, &flag);
return actual;
}
/******************************************************************************
* serial_write()
* Attempt to write N bytes from the serial device, stopping as soon
* as unable to send more without waiting. Return:
* 0-N Actual number of bytes written, 0 if none.
* -1 error condition
******************************************************************************/
static int
serial_write( port, bufptr, want )
int port;
char *bufptr;
int want;
{
int i;
for (i = 0; i < want; i++){
sputch(*bufptr++);
}
return want;
}
/******************************************************************************
* set_mode()
* Set the serial device to a specified operating mode.
* Return 0 on success, -1 on failure
******************************************************************************/
static int
set_mode(baud,ivec)
unsigned long baud; /* Buad rate */
unsigned ivec; /* Interrupt vector */
{
int val;
if ((val = xltbaud(baud, baudmap)) <= 0){
return 0;
}
_outbyte( LCR_REG, ENCNTREG ); /* enable count regs */
_outbyte( LCNT_REG, (unsigned char)val);
_outbyte( HCNT_REG, (unsigned char)(val >>8) );
_outbyte( LCR_REG, CHLEN1 | CHLEN0 ); /* n,8,1 */
_outbyte( MCR_REG, OUT2 | RTS | DTR );
if (baud >= 57600) {
/* Enable receive and transmit FIFOs.
*
* FCR<7:6> 00 trigger level = 1 byte
* FCR<5:4> 00 reserved
* FCR<3> 0 mode 0 - interrupt on data ready
* FCR<2> 0
* FCR<1> 0
* FCR<0> 1 turn on fifo mode
*/
_outbyte(ISR_REG, 0x01);
}
/* initialize serial queue and chip */
siq.q_cnt = siq.q_ovfl = 0;
siq.q_in = siq.q_out = siq.q_buf;
init_8259(ivec);
_outbyte(IER_REG, ENRXINT); /* enable ints */
_outbyte(IER_REG, ENRXINT); /* do twice because of chip bug */
return 1;
}
/******************************************************************************
* S_INT()
* C serial interrupt routine (can't use debug).
* Assumes that only Data ready interrupt is enabled.
******************************************************************************/
#pragma interrupt(S_INT)
void
S_INT()
{
int c;
register struct queue *qp = &siq;
int pif;
pif = change_if(0);
while (_inbyte(ST_REG) & GETRDYBIT) {
c = _inbyte(serial_iobase);
if (qp->q_cnt >= (QBUFSZ-1)) {
qp->q_ovfl++;
return;
}
*qp->q_in++ = c;
qp->q_cnt++;
if (qp->q_in >= &qp->q_buf[QBUFSZ]){
qp->q_in = qp->q_buf; /* pre-advance pointer */
}
}
_outbyte(0x20, 0x20);
change_if(pif);
}
/******************************************************************************
* init_8259()
* Initailize the PCDOS 8259 serial vector for interrupt driven usage.
* return Boolean indicating success.
******************************************************************************/
static void
init_8259(ivec)
unsigned ivec; /* Interrupt vector */
{
int pif;
if (!orig_serial_vec) {
orig_serial_vec = _dos_getvect(ivec);
_dos_setvect(ivec, S_INT);
}
pif = change_if( 0 );
_outbyte( IMR_8259, (_inbyte(IMR_8259) & ~(1 << ivec - 8)) );
change_if( pif );
}
/******************************************************************************
* uinit_8259()
* Unset the PCDOS 8259 interrupt controller serial interrupt vector
* used for interrupt mode. Return Boolean indicating success.
******************************************************************************/
static void
uinit_8259(ivec)
unsigned ivec; /* Interrupt vector */
{
int pif;
pif = change_if( 0 );
_outbyte( IMR_8259, (_inbyte(IMR_8259) | (1 << ivec - 8)) ); /* mask */
change_if( pif );
if (orig_serial_vec) {
_dos_setvect(ivec, orig_serial_vec);
orig_serial_vec = 0;
}
}
/******************************************************************************
* get_port()
* equipment check bios call (bit #)
* 15-14 printers
* 12 game io
* 11-9 com devices
* 7-6 disk drives (0=1)
* 5-4 init video mode
* 3-2 ram size
* 0 are any disk drives
******************************************************************************/
static int
get_port(comwhich)
int comwhich;
{
union REGS regs;
int combase;
int86( 0x11, ®s, ®s );
if (comwhich > ((regs.x.ax >> 9) & 7)){
return 0;
}
combase = ((unsigned short *)0x400)[comwhich-1];
return combase;
}
int
change_if(int f)
{
volatile unsigned int old_f = _getflags();
_setflags(f ? old_f | _FLAG_INTERRUPT : old_f & ~_FLAG_INTERRUPT);
return (old_f & _FLAG_INTERRUPT) != 0;
}
[LISTING TWO]
/***************************************************************************
* open_port()
* This routine is used to interface from the System V "open" format
* to the serial driver for the dos port we have provided.
*
* Because of the interface to the driver, this routine does not
* do the final open of the port. Instead, it sets up a data structure
* that is filled with pertinent data. The data structure is further
* filled by subsequent ioctl() calls. When the program actually
* requests a read or write, the the port is implicitly reopened with
* the correct values.
*
* Returns:
* I/O register base value; -1 if error
***************************************************************************/
int
open_port(port_id, mode)
char *port_id; /* Name of serial port to open */
int mode; /* mode parameter not used */
{
if (port_id == NULL){
return -1;
}
if ((strlen(port_id) == 4)
&& (!strcspn(port_id, "com") || !strcspn(port_id, "COM"))) {
strcpy(serial_device, port_id);
serial_iobase = 0;
serial_ivec = 0;
serial_baud = B9600>>16;
serial_opened = 1;
serial_setup = 1;
serial_updated = 0 ;
return serial_open() == -1 ? 0 : (int)serial_iobase;
}
return open(port_id, mode);
}
static int
serial_check()
{
if (!serial_setup){
return 0;
}
if (serial_updated) {
close_port(serial_iobase);
serial_setup = 1;
}
if (!serial_opened) {
serial_opened = 1;
if (serial_open() == -1){
return 0;
}
}
return 1;
}
/***************************************************************************
* read_port()
* This routine is used to read from the serial port of an MS/DOS
* system. It interfaces to a driver that handles all port communication.
*
* First, the routine checks to see if the port has been opened for
* use yet. If not, it opens the device and prepares it for reading.
* If so, it passes the information on to the serial driver to actually
* get the data.
*
* Returns:
* int number of bytes read, or -1 if error
***************************************************************************/
int
read_port(devindex, buffer, n, timeout)
unsigned int devindex; /* Index into device array for port */
char *buffer; /* Buffer for data read from port */
int n; /* Number of bytes to read */
int timeout; /* Timeout value in seconds */
/* The low lever driver is */
/* expecting milliseconds. */
{
if (!buffer){
return -1;
} else if (is_serial(devindex)) {
return serial_check() ?
serial_read(serial_iobase, buffer, n, 1000*timeout):-1;
} else {
/* Not the serial device, do normal I/O */
if (devindex == fileno(stdin)){
return -2;
} else {
read(devindex, buffer, n);
}
}
}
/***************************************************************************
* write_port()
* This routine is used to write to the serial port of an MS/DOS
* system. It interfaces to a driver that handles all port communication.
*
* First, the routine checks to see if the port has been opened for
* use yet. If not, it opens the device and prepares it for writing.
* If so, it passes the information on to the serial driver to actually
* send the data.
*
* Returns:
* int number of bytes actually written; -1 if error.
***************************************************************************/
int
write_port(devindex, buffer, n)
unsigned int devindex; /* Index into device array for port */
char *buffer; /* Buffer of data to write to port */
int n; /* Number of bytes to write */
{
if (!buffer){
return -1;
} else if (is_serial(devindex)) {
return serial_check() ?
serial_write(serial_iobase,buffer,n) : -1;
} else {
/* Not the serial device */
return write(devindex, buffer, n);
}
}
/***************************************************************************
* close_port()
* This routine is used to interface from the System V "close" format
* to the serial driver for the dos port we have provided.
***************************************************************************/
close_port(devindex)
unsigned int devindex; /* serial port to close */
{
if (is_serial(devindex)) {
serial_close();
serial_opened = 0;
serial_setup = 0;
serial_updated = 0;
} else {
close(devindex);
}
}
[LISTING THREE]
/***************************************************************************
* dup2_port()
* This routine is emulates the System V "dup2" function for our
* DOS serial driver. It causes all reads or writes to fd2 to be
* issued as if they were thru fd1.
***************************************************************************/
dup2_port(fd1, fd2)
unsigned int fd1; /* file descriptor to be duplicated */
unsigned int fd2; /* file descriptor to be replaced */
{
if (fd1 != serial_iobase) {
/*
* fd1 is not assigned to the serial port:
* we can ship this off to the real dup2() routine.
*/
dup2(fd1, fd2);
} else {
/*
* Keep track of the number of dup'ed fds seen so far
*/
if (serial_dup.fd == NULL) {
serial_dup.fd = (int*)
malloc(serial_dup.max * sizeof(int*));
}
if (serial_dup.num >= serial_dup.max) {
serial_dup.max *= 2;
serial_dup.fd = (int*) realloc(serial_dup.fd,
serial_dup.max * sizeof(int*));
}
serial_dup.fd[serial_dup.num++] = fd2;
}
}
/**********************************************************************
* is_dupd();
* Return true (1) iff fd is a dup2'd file descriptor.
**********************************************************************/
static int
is_dupd(fd)
int fd;
{
int i;
for (i = 0; i < serial_dup.num; i++) {
if (serial_dup.fd[i] == fd){
return 1;
}
}
return 0;
}
Figure 1: Macros to handle text and binary files in DOS.
#ifdef DOS
# define READ_BIN "rb"
# define READ_TXT "r"
# define WRITE_BIN "wb"
# define WRITE_TXT "w"
#else /* the unix way */
# define READ_BIN "r"
# define READ_TXT "r"
# define WRITE_BIN "w"
# define WRITE_TXT
#endif /* DOS */
Figure 2: Mapping the UNIX SIGALRM to a DOS user-defined signal.
#ifdef DOS
# define SIGALRM SIGUSR1
#endif /*DOS*/
Figure 3. Writing files using a specified file handle.
write_files(fp, buffer, bytes)
int fp;
char *buffer;
int bytes;
{
if (bytes > 0) write(fp, buffer, bytes);
}
Figure 4: Mapping WRIT_TTY to either DOS or UNIX I/O calls.
#ifdef DOS
# define WRITE_TTY write_port
#else /* unix */
# define WRITE_TTY write
#endif /* DOS */