[comp.os.minix] tidied up BMS clock driver

wheels@mks.UUCP (Gerry Wheeler) (12/07/88)

I have been working on the BMS clock driver that was posted a couple of
weeks ago, with help from Frans Meulenbrook
(mcvax!cst.prl.philips.nl!meulenbr).  I made some changes to the
original driver.  Some were cosmetic, but I also removed definitions of
variables that were never used.  Probably the biggest change is that it
now returns a count that reflects the number of bytes it actually
provides.  Previously it would return as many bytes as the caller asked
for, most of which were junk.  There was also a section of code near the
end of the driver that did a lot of bit twiddling with the hardware, but
which had no effect on what was given to the caller.  I deleted it with
no harmful side effects. 

Once that was running, Frans gave me a program based on the clock
program for the Mega ST which had been modified to read the data from
the BMS driver (/dev/bms) and used it to set the system time.  However,
it seemed to have a lot of stuff in it, like converting yymmddhhmmss to
seconds since Jan.  1970, and it had to know about leap years and all
that ugly stuff.  Most of that stuff is already in the date command, so
I wrote another version which simply reads from /dev/bms and prints the
data in a format acceptable to date.  Now my /etc/rc simply has a line
like this:

	date `bmsrtc`

Since the information on how to put this driver into MINIX has already
been posted, I won't cover that again.  However, if someone needs the
info, I have a copy of that posting here I can forward. 

------------------------------ bmsclock.c ------------------------------

#ifdef atari_st
/*
 * this file contains a bms-100 clock driver
 * on the atari st.
 *
 * the driver supports two operations: read a bytes and write a bytes.
 * it accepts two messages, one for reading and one for writing,
 * both using message format m2 and with the same parameters:
 *
 *    m_type      device    proc_nr     count    position  adrress
 * ---------------------------------------------------------------
 * |  bms_read | device  | proc nr |  bytes  |  offset | buf ptr |
 * |-----------+---------+---------+---------+---------+---------|
 * | bms_write | device  | proc nr |  bytes  |  offset | buf ptr |
 * ---------------------------------------------------------------
 *
 * the file contains one entry point:
 *
 *	bmsclock_task: main entry when system is brought up
 *
 *
 */

#include "../h/const.h"
#include "../h/type.h"
#include "../h/callnr.h"
#include "../h/com.h"
#include "../h/error.h"
#include "const.h"
#include "type.h"
#include "proc.h"

#include "staddr.h"
#include "stmfp.h"
#include "stdma.h"

#define max_errors 3
#define bms_count  13	/* number of bytes to transfer */

#define trace(x)	/* x */
#define debug(x)	x

/* dis/enable interrupts */
#define ienable()	mfp->mf_ierb |= ib_dint
#define idisable()	mfp->mf_ierb &= ~ib_dint

private message mess;		/* message buffer for in and out */


/*===================================================================*
 *				bmsclock_task			     *
 *===================================================================*/
public bmsclock_task()
{
	register r, caller, procno;


	/*
	 * the main loop of the bms clock task.
 	 * it waits for a message, carries it out, and sends a reply.
	 */
  
	while (true) {
		receive(any, &mess);
		if (mess.m_source < 0)
			panic("bms clock task got message from ",
							mess.m_source);
		trace(printf("bms: received %d from %d\n",
						mess.m_type,mess.m_source));
		caller = mess.m_source;
		procno = mess.proc_nr;

		/* now carry out the work. */
		switch (mess.m_type) {
			case bms_read:
			case bms_write:
				r = do_bms(&mess);
				break;
			default:
				r = einval;
				break;
		}

		/* finally, prepare and send the reply message. */
		mess.m_type = task_reply;	
		mess.rep_proc_nr = procno;
		mess.rep_status = r;	/* # of bytes or error code */
		send(caller, &mess);	/* send reply to caller */
	}
}



/*===================================================================*
 *				do_bms  			     *
 *===================================================================*/
private int do_bms(mp)
register message *mp;
{
	register struct proc *rp;
	register r, errors, count, rw, minor;
	register phys_bytes address;
	extern phys_bytes umap();

	rw = mp->m_type;

	minor = mp->device;
	count = mp->count;
	if (count <= 0)
		return(eof);
	rp = proc_addr(mp->proc_nr);
	address = umap(rp, d, (vir_bytes) mp->address, (vir_bytes) count);
	if (address == 0)
		return(einval);
	trace(printf("bms%d: %s: cnt=%d\n", minor,
				rw == disk_read ? "read" : "write", count));

	rp->p_physio = 1;		/* disable (un)shadowing */

	/* this loop allows a failed operation to be repeated. */
	for (errors = 0; errors < max_errors; errors++) {
		r = do_xbms(address, count, rw);
		if (r >= 0)
			break;		/* if successful, exit loop */
	}
	rp->p_physio = 0;		/* enable (un)shadowing */
	return(r);
}

/*===================================================================*
 *				do_xbms  			     *
 *===================================================================*/

private int do_xbms(address, count, rw)
phys_bytes 	address;
int		count;
int		rw;
{

	register int r;
	extern void dmaint();


	/*
	 * carry out the transfer. all parameters have been checked and
	 * are set up properly. called with address, count (may be changed
	 * when reading), and rw flag. returns count of bytes transferrred
	 * (maximum of bms_count for reading) or an error.
	 *
	 * every single byte written to the hdc will cause an interrupt.
 	 * Thus disable interrupts while communicating with hdc. Ready test
	 * will be done by busy waiting. Only for real hard disk operations
	 * interrupts will be enabled.
	 */

	TRACE(printf("bms address:0x%X count=%d cmd:%s\n",
			address, count, (rw==DISK_READ)?"READ":"WRITE"));

	IDISABLE();
	dmagrab(BMSCLOCK, dmaint);

	if (rw == DISK_READ) {
		r = read_clock(address, count);
	}
	else if (rw = DISK_WRITE) {
		/* reserve for setting clock */
		r = EINVAL;
	}
	else {
		/* reserve for screen blanker */
		r = EINVAL;
	}

	dmafree(BMSCLOCK);
	IENABLE();

	return r;
}

#define	DAYH  8
#define HOURH 5

PRIVATE int read_clock(address, count)
phys_bytes address;
int count;
{
	char lbuf[BMS_COUNT];
	register int r, s, index;
	register char *cp;

	if (count > BMS_COUNT)
		count = BMS_COUNT;

	DMA->dma_mode = FDC | HDC;
	DMA->dma_data = 0xe1;
	for(r = 100; r > 0; r--)
		/* pause */;
	DMA->dma_mode = FDC | HDC;
	DMA->dma_data = 0xe5;

	/*
	 * Read the data into the local buffer.
	 */

	for(index = 0; index < count; index++) {
		DMA->dma_mode = FDC | HDC | A0;
		DMA->dma_data = (index << 4);
		for(r = 3; r > 0; r--)
			/* pause */;
		lbuf[index] =  DMA->dma_data & 0xf;
	}
	DMA->dma_mode = FDC | HDC;
	DMA->dma_data = 0xe0;
	DMA->dma_mode = FDC;

	/*
	 * Some digits contain flag bits as well as time; mask
	 * out the extra bits.
	 */

	lbuf[HOURH] &= 0x03;
	lbuf[DAYH]  &= 0x03;

	/*
	 * Transfer the local buffer to the final address.
	 */

	for(index = 0, cp = (char*)address; index < count; index++)
		*cp++ = lbuf[index];

	return count;
}

#endif /* ATARI_ST */

------------------------------ bmsrtc.c ------------------------------

/*
 * A program to read /dev/bms and print the results to stdout.
 * The format of the output is the same as the input to the
 * date command, so one can use a command like
 *
 *	date `bmsrtc`
 *
 * in /etc/rc to set the date.
 *
 * This program can later be extended to accept input and write
 * to /dev/bms to set the time. Its input might be made compatible
 * with the output of date, so one could use a command like
 *
 *	bmsrtc `date`
 *
 * to copy the system date into the bms clock.
 */

#include <stdio.h>

#define	SECL	0
#define	SECH	1
#define	MINL	2
#define	MINH	3
#define	HOURL	4
#define	HOURH	5
#define	WDAY	6
#define	DAYL	7
#define	DAYH	8
#define	MONL	9
#define	MONH	10
#define	YEARL	11
#define	YEARH	12

#define	BUFSIZE	13

main(argc, argv)
int argc;
char *argv[];
{
	register int fd, count, year;
	char buf[BUFSIZE];

	fd = open("/dev/bms", 0);
	if (fd < 0) {
		fprintf(stderr, "%s: cannot open /dev/bms", argv[0]);
		exit(1);
	}

	count = read(fd, buf, BUFSIZE);
	close(fd);
	if (count != BUFSIZE) {
		fprintf(stderr, "%s: cannot read /dev/bms", argv[0]);
		exit(1);
	}

	/*
	 * The year digits from the clock are 80 less than they
	 * should be. Add a correction.
	 */

	year = buf[YEARH] * 10 + buf[YEARL] + 80;

	printf("%d%d%d%d%02d%d%d%d%d%d%d\n", buf[MONH], buf[MONL],
			buf[DAYH], buf[DAYL], year,
			buf[HOURH], buf[HOURL], buf[MINH], buf[MINL],
			buf[SECH], buf[SECL]);

	exit(0);
}


-- 
     Gerry Wheeler                           Phone: (519)884-2251
Mortice Kern Systems Inc.               UUCP: uunet!watmath!mks!wheels
   35 King St. North                             BIX: join mks
Waterloo, Ontario  N2J 2W9                  CompuServe: 73260,1043