[comp.os.minix] minix/st bms-200 clock driver

hcj@lzaz.ATT.COM (HC Johnson) (01/08/89)

This is a shar of 5 files to add BMS200 support to the BMS100 clock
driver I posted earlier.
User programs:
Makefile	- to make bmsset and bmsrtc
bmsset.c	- set the BMS clock 100 to 200 from the current date
bmsrtc.c	- set the current date from the BMS 100 or 200 clock.

kernel/ programs:
bmsclock.c	- clock driver for bms 100 and 200
bms200.s	- support for driver

Add bms200.s to the Makefile in kernel/ in the manner of stmpx.s.

Add the command bmsrtc to the etc/rc script.

In /dev make a Inode for bms, with minor 0 for bms100 and 1 for bms200.

Howard Johnson
ATT-BL
lzaz!hcj


================shar follows=================
echo x - Makefile
gres '^X' '' > Makefile << '/'
Xall: bmsrtc bmsset
Xbmsrtc:	bmsrtc.c
X	cc -O bmsrtc.c -o bmsrtc
X	chmem =2000 bmsrtc
Xbmsset:	bmsset.c
X	cc -O bmsset.c -o bmsset
X	chmem =2000 bmsset
/
echo x - bms200.s
gres '^X' '' > bms200.s << '/'
X        .define _rd1byte
X        .define _wr1byte
X
X        .sect   .text
X        .sect   .rom
X        .sect   .data
X        .sect   .bss
X
X        .sect   .text
X
Xdma_base        =               0xff8604
Xdma_data        =               0
Xdma_cont        =               2
X
Xselect          =               0x10
Xsck_en          =               0x08
X
Xwrite           =               0x80
X
Xclk_dev =               6
Xclk_adr =               clk_dev*32
X!
X! moves the byte in d0 to the clock chip
X!  uses d1, a0 must point to the dma channel
X!
X
X_wr1byte:
X        clr.w   d0
X        move.b  5(a7), d0
X        lea     dma_base, a0
X        move.w          #7, d1
X        swap            d0
X        move.w          #clk_adr+select+sck_en, d0
X
Xwrloop:         lsr.w           #1, d0
X        swap            d0
X
X        roxl.b          #1, d0
X        swap            d0
X        roxl.w          #1, d0
X        move.w          d0, dma_data(a0)
X
X        dbra            d1, wrloop
X
X        rts
X
X!
X! moves a byte into d0 from the clock chip
X!  uses d1, a0 must point to the dma channel
X!
X
X_rd1byte:
X        lea     dma_base, a0
X        move.w          #7, d1
X
Xrdloop:         swap            d0
X
X        move.w          #clk_adr+select+sck_en, dma_data(a0)
X        move.w          dma_data(a0), d0
X
X        roxr.b          #1, d0
X        swap            d0
X        roxl.b          #1, d0
X
X        dbra            d1, rdloop
X
X        rts
X
/
echo x - bmsclock.c
gres '^X' '' > bmsclock.c << '/'
X#ifdef ATARI_ST
X/*
X * This file contains a bms-100 clock driver
X * This file contains a bms-200 clock driver
X * on the Atari ST.
X *
X * The driver supports two operations: read a bytes and write a bytes.
X * It accepts two messages, one for reading and one for writing,
X * both using message format m2 and with the same parameters:
X *
X *    m_type      DEVICE    PROC_NR     COUNT    POSITION  ADRRESS
X * ----------------------------------------------------------------
X * |  BMS_READ | device  | proc nr |  bytes  |  offset | buf ptr |
X * |------------+---------+---------+---------+---------+---------|
X * | BMS_WRITE | device  | proc nr |  bytes  |  offset | buf ptr |
X * ----------------------------------------------------------------
X *
X * The file contains one entry point:
X *
X *	bmsclock_task: main entry when system is brought up
X *
X *	device = 0: bms100
X *	device = 1: bms200
X *
X */
X
X#include "../h/const.h"
X#include "../h/type.h"
X#include "../h/callnr.h"
X#include "../h/com.h"
X#include "../h/error.h"
X#include "const.h"
X#include "type.h"
X#include "proc.h"
X
X#include "staddr.h"
X#include "stmfp.h"
X#include "stdma.h"
X#define MAX_ERRORS 3
X
X#define TRACE(x)	/* x */
X#define DEBUG(x)	x
X
X/* dis/enable interrupts */
X#define IENABLE()	MFP->mf_ierb |= IB_DINT
X#define IDISABLE()	MFP->mf_ierb &= ~IB_DINT
X
X/* timing constants */
X
XPRIVATE message mess;		/* message buffer for in and out */
X
X#define XFERSIZE 13
X
X/*===========================================================================*
X *				bmsclock_task				     *
X *===========================================================================*/
XPUBLIC bmsclock_task()
X{
X  register	r, drive, caller, procno;
X
X
X  /*
X   * The main loop of the bms clock task.
X   * It waits for a message, carries it out, and sends a reply.
X   */
X  while (TRUE) {
X	receive(ANY, &mess);
X	if (mess.m_source < 0)
X		panic("bms clock task got message from ", mess.m_source);
X	TRACE(printf("bms: received %d from %d\n",mess.m_type,mess.m_source));
X	caller = mess.m_source;
X	procno = mess.PROC_NR;
X
X	/* Now carry out the work. */
X	switch (mess.m_type) {
X	    case BMS_READ:
X	    case BMS_WRITE:	r = do_bms(&mess);	break;
X	    default:		r = EINVAL;		break;
X	}
X
X	/* Finally, prepare and send the reply message. */
X	mess.m_type = TASK_REPLY;	
X	mess.REP_PROC_NR = procno;
X	mess.REP_STATUS = r;	/* # of bytes transferred or error code */
X	send(caller, &mess);	/* send reply to caller */
X  }
X}
X
X
X
X/*===========================================================================*
X *				do_bms  				     *
X *===========================================================================*/
XPRIVATE int do_bms(mp)
Xregister message *mp;
X{
X  register struct proc	*rp;
X  register		r, errors, count, rw, drive, minor;
X  register long		secnum;
X  register phys_bytes	address;
X  extern phys_bytes	umap();
X
X  rw = mp->m_type;
X
X  minor = mp->DEVICE;
X  count = mp->COUNT;
X  if (count <= 0)
X	return(EOF);
X  if (count > XFERSIZE)
X	count = XFERSIZE;
X  rp = proc_addr(mp->PROC_NR);
X  address = umap(rp, D, (vir_bytes) mp->ADDRESS, (vir_bytes) count);
X  if (address == 0)
X	return(EINVAL);
X  TRACE(printf("hd%d: %s: sec=%D; cnt=%d\n",
X	minor, rw == DISK_READ ? "read" : "write", secnum, count));
X  rp->p_physio = 1;		/* disable (un)shadowing */
X  /* This loop allows a failed operation to be repeated. */
X  for (errors = 0; errors < MAX_ERRORS; errors++) {
X	r = do_xbms(address, count, rw, minor?1:0);
X	if (r == OK)
X		break;		/* if successful, exit loop */
X  }
X  rp->p_physio = 0;		/* enable (un)shadowing */
X  if (r != OK)
X	return(EIO);
X  return(count );
X}
X
X/*===========================================================================*
X *				do_xbms  				     *
X *===========================================================================*/
X
X/* defines for bms 100 */
X#define CLK_100	0x7
X#define CLK_ADR1	(CLK_100 * 32)
X#define CLK_HOLD	0x1
X#define CLK_WRITE	0x2
X#define CLK_READ	0x4
X#define CLK_WREN	0x8
X/* defines for bms 200 */
X#define SELECT	0x10
X#define SCK_EN	0x08
X#define SCK_WRITE	0x80
X#define CLK_SCSI	0x6
X#define CLK_ADR	(CLK_SCSI * 32)
Xextern int wr1byte();
Xextern int rd1byte();
X
XPRIVATE int do_xbms(address, count, rw, minor)
Xphys_bytes 	address;
Xint		count;
Xint		rw;
Xint		minor; /* 0= bms100 1=bms200 */
X{
X
X  register	r, s, wrbit;
X  register	index;
X  register	char *cp;
X  char	lbuf[XFERSIZE];
X  extern void	dmaint();
X
X
X  /*
X   * Carry out the transfer. All parameters have been checked and
X   * are set up properly.
X   *
X   * Every single byte written to the hdc will cause an interrupt.
X   * Thus disable interrupts while communicating with hdc. Ready test
X   * will be done by busy waiting. Only for real hard disk operations
X   * interrupts will be enabled.
X   */
X  TRACE(printf("hd address:0x%X count=%d minor=%d cmd:%s\n",
X	address, count, minor, (rw==0)?"READ":"WRITE")
X  );
X  IDISABLE();
X
X  dmagrab(BMSCLOCK, dmaint);
X  if (rw == DISK_READ && minor == 0) {
X	DMA->dma_mode = FDC | HDC;
X	DMA->dma_data = CLK_ADR1+CLK_HOLD;
X	for(r=100;r>0;r--) ; /* pause */
X	DMA->dma_mode = FDC | HDC;
X	DMA->dma_data = CLK_ADR1+CLK_READ+CLK_HOLD;
X	for(index = 0; index<XFERSIZE; index++) {
X		DMA->dma_mode = FDC | HDC | A0;
X		DMA->dma_data = (index << 4);
X		for(r=3;r>0;r--) ; /* pause */
X		lbuf[index] =  DMA->dma_data & 0xf;
X	}
X	DMA->dma_mode = FDC | HDC;
X	DMA->dma_data = CLK_ADR1+0;
X	DMA->dma_mode = FDC ;
X
X	for(index=0,cp=(char*)address; index<count; index++)
X		*cp++ = lbuf[index];
X	r = 0; /* good return */
X
X  } else if (rw == DISK_READ && minor == 1) {
X	DMA->dma_mode = FDC | HDC;
X	DMA->dma_data = CLK_ADR+SELECT;
X	wr1byte(0x20);
X
X	for(index = 0; index<7; index++) {
X		lbuf[index] =  rd1byte();
X	}
X	DMA->dma_data = CLK_ADR+0;
X	DMA->dma_mode = FDC ;
X
X	cp=(char*)address;
X/* lbuf format: bcd H L */
X	*cp++ = lbuf[0] & 0xf;
X	*cp++ = (lbuf[0]  >> 4) & 0xf;
X	*cp++ = lbuf[1] & 0xf;
X	*cp++ = (lbuf[1]  >> 4) & 0xf;
X	*cp++ = lbuf[2] & 0xf;
X	*cp++ = (lbuf[2]  >> 4) & 0xf;
X	*cp++ = lbuf[3] & 0xf;
X	*cp++ = lbuf[4] & 0xf;
X	*cp++ = (lbuf[4]  >> 4) & 0xf;
X	*cp++ = lbuf[5] & 0xf;
X	*cp++ = (lbuf[5]  >> 4) & 0xf;
X	*cp++ = lbuf[6] & 0xf;
X	*cp++ = (lbuf[6]  >> 4) & 0xf;
X	r = 0; /* good return */
X
X  } else if (rw == DISK_WRITE && minor == 0) {
X
X
X	for(index=0,cp=(char*)address; index<count; index++)
X		lbuf[index] = *cp++;
X	
X	DMA->dma_mode = FDC | HDC;
X	DMA->dma_data = CLK_ADR1+CLK_WREN+CLK_HOLD;
X	for(r=100;r>0;r--) ; /* pause */
X	for(index = 0; index<13; index++) {
X		DMA->dma_mode = FDC | HDC | A0;
X		DMA->dma_data = (index << 4) | (lbuf[index] & 0xf);
X		for(r=1;r>0;r--) ; /* pause */
X		DMA->dma_mode = FDC | HDC;
X		DMA->dma_data = CLK_ADR1+CLK_WREN+CLK_HOLD+CLK_WRITE;
X		for(r=3;r>0;r--) ; /* pause */
X		DMA->dma_mode = FDC | HDC;
X		DMA->dma_data = CLK_ADR1+CLK_WREN+CLK_HOLD;
X	}
X	DMA->dma_mode = FDC | HDC;
X	DMA->dma_data = CLK_ADR1+CLK_HOLD;
X	for(r=100;r>0;r--) ; /* pause */
X	DMA->dma_mode = FDC | HDC;
X	DMA->dma_data = CLK_ADR1+CLK_READ+CLK_HOLD;
X
X	for(index = 0; index<13; index++) {
X		DMA->dma_mode = FDC | HDC | A0;
X		DMA->dma_data = (index << 4);
X		for(r=3;r>0;r--) ; /* pause */
X		s =  (unsigned char)DMA->dma_data & 0xf;
X		if(s != lbuf[index]) { /* fail */
X			DMA->dma_mode = FDC | HDC;
X			DMA->dma_data = CLK_ADR1+0;
X			DMA->dma_mode = FDC ;
X			r = EIO; /* bad return */
X			goto bms_exit;
X		}
X	}
X	DMA->dma_mode = FDC | HDC;
X	DMA->dma_data = CLK_ADR1+0;
X	DMA->dma_mode = FDC ;
X	r = 0; /* good return */
X  } else if (rw == DISK_WRITE && minor == 1) {
X	int l;
X
X	cp=(char*)address;
X	l = *cp++ & 0xf; /* sec-L */
X	lbuf[0] = (*cp++ << 4) | l;
X	l = *cp++ & 0xf; /* min-L */
X	lbuf[1] = (*cp++ << 4) | l;
X	l = *cp++ & 0xf; /* hr-L */
X	lbuf[2] = (*cp++ << 4) | l;
X	lbuf[2] &= 0x7f;	/* strip the 24 hour bit */
X	lbuf[3] = *cp++ & 0xf;
X	l = *cp++ & 0xf; /* day-L */
X	lbuf[4] = (*cp++ << 4) | l;
X	l = *cp++ & 0xf; /* mon-L */
X	lbuf[5] = (*cp++ << 4) | l;
X	l = *cp++ & 0xf; /* yr-L */
X	lbuf[6] = (*cp++ << 4) | l;
X	
X	DMA->dma_mode = FDC | HDC;
X	DMA->dma_data = CLK_ADR+SELECT;
X	wr1byte(SCK_WRITE+0x31);
X	wr1byte(0xb5);
X	wr1byte(0);
X	DMA->dma_data = CLK_ADR+0;
X
X	DMA->dma_mode = FDC | HDC;
X	DMA->dma_data = CLK_ADR+SELECT;
X	wr1byte(SCK_WRITE+0x20);
X	for(index = 0; index<7; index++) {
X		wr1byte(lbuf[index]);
X	}
X	DMA->dma_data = CLK_ADR+0;
X
X	DMA->dma_mode = FDC | HDC;
X	DMA->dma_data = CLK_ADR+SELECT;
X	wr1byte(0x20);
X
X	for(index = 0; index<7; index++) {
X		if(lbuf[index] !=  rd1byte()) { /* fail */
X			DMA->dma_data = CLK_ADR+0;
X			DMA->dma_mode = FDC ;
X			r = EIO; /* bad return */
X			goto bms_exit;
X		}
X	}
X	DMA->dma_data = CLK_ADR+0;
X	DMA->dma_mode = FDC ;
X	r = 0; /* good return */
X  } else {
X/* reserve for screen blanker */
X	r = EIO;
X	goto bms_exit;
X  }
Xbms_exit:
X  dmafree(BMSCLOCK);
X    IENABLE();
X  
X  return(r);
X}
/
echo x - bmsrtc.c
gres '^X' '' > bmsrtc.c << '/'
X#define	DEBUG
X
X#define	AD_RTC	0xFFFC20
X
X#define	SECL	0
X#define	SECH	1
X#define	MINL	2
X#define	MINH	3
X#define	HOURL	4
X#define	HOURH	5
X
X#define	DAYL	7
X#define	DAYH	8
X#define	MONL	9
X#define	MONH	10
X#define	YEARL	11
X#define	YEARH	12
X
X#define	N	13
X
Xint	fd;
Xunsigned char	a[N];
Xint	m[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
X
Xlong	time();
Xchar	*ctime();
X
Xmain(argc, argv)
Xchar **argv;
X{
X	register i, n, d, ok;
X	long ot, nt;
X#ifdef DEBUG
X	int debug = argc != 1;
X#endif
X
X	fd = open("/dev/bms", 0);
X	if (fd < 0)
X		fatal("cannot open /dev/bms ");
X	for(n=0;n<3;n++) {
X		i = read(fd,a,N);
X		if(i <0) {
X			perror("read");
X			printf("read returned %d\n",i);
X			exit(1);
X		}
X		if (a[SECL] == 15)
X			fatal("no RTC present");
X		break;
X	}
Xif(debug)
X	printf("bms: %x %x %x %x  %x %x %x %x  %x %x %x %x %x\n",
X	  a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9],a[10],a[11],a[12]);
X	a[HOURH] &= 3;
X	nt = 0;
X	i = dd(YEARL, 0, 99);
X	if ((i & 3) == 0)
X		m[1]++;
X	i += 80;
X	while (--i >= 70) {
X		nt += 365;
X		if ((i & 3) == 0)
X			nt++;
X	}
X	i = dd(MONL, 1, 12)-1;
X	while (--i >= 0)
X		nt += m[i];
X	nt += dd(DAYL, 1, 31) - 1;
X	nt *= 24;
X	nt += dd(HOURL, 0, 23);
X	nt *= 60;
X	nt += dd(MINL, 0, 59);
X	nt *= 60;
X	nt += dd(SECL, 0, 59);
X	time(&ot);
X#ifdef DEBUG
X	if (debug) {
X		printf("old time = (%ld) %s\n", ot, ctime(&ot));
X		printf("new time = (%ld) %s\n", nt, ctime(&nt));
X	}
X#endif
X	if (ot > nt)
X		fatal("cannot set time back\n");
X	if (stime(&nt) < 0)
X		fatal("stime() failed");
X	exit(0);
X}
X
Xdd(i, min, max)
X{
X	register n;
X
X	n = a[i] + 10 * a[i+1];
X	if (n < min || n > max)
X		fatal("ASSERT(idx=%d %d >= %d && %d <= %d)", i, n, min, n, max);
X	return(n);
X}
X
X
Xfatal(s, a1, a2, a3, a4, a5)
Xchar *s;
X{
X	printf("bmsrtc: ");
X	printf(s, a1, a2, a3, a4, a5);
X	printf(" (fatal)\n");
X	exit(1);
X}
/
echo x - bmsset.c
gres '^X' '' > bmsset.c << '/'
X/* bmsset - r set the date	in bms	Author: Howard Johnson
X * taken from minix date, Author: Adri Koppes
X */
X
X
X#define	SECL	0
X#define	SECH	1
X#define	MINL	2
X#define	MINH	3
X#define	HOURL	4
X#define	HOURH	5
X
X#define DAYW	6
X
X#define	DAYL	7
X#define	DAYH	8
X#define	MONL	9
X#define	MONH	10
X#define	YEARL	11
X#define	YEARH	12
X
X#define	N	13
X
Xint	fd;
Xunsigned char	a[N];
X
Xint days_per_month[] =
X  { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
X
Xstruct {
X	int year, month, day, hour, min, sec;
X} tm;
X
Xlong s_p_min;
Xlong s_p_hour;
Xlong s_p_day;
Xlong s_p_year;
Xint leap = 0;
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X  long t, time();
X  int n,i;
X
X  s_p_min = 60;
X  s_p_hour = 60 * s_p_min;
X  s_p_day = 24 * s_p_hour;
X  s_p_year = 365 * s_p_day;
X
X	time(&t);
X	cv_time(t);
X/* expected fmt
XThu Nov 10 13:59:34 EST 1988
X.month 0-11
X.day 1-31
X.hour 0-23
X.minute 0-59
X.second 0-59
X.year 1970 - 
X*/
X	tm.year -= 1980;
X	tm.month += 1;
X
X	
X	a[SECL] = (tm.sec % 10) & 0xf;
X	a[SECH] = (tm.sec / 10) & 0xf;
X	a[SECL] = 0;
X	a[SECH] = 0;
X	a[MINL] = (tm.min % 10) & 0xf;
X	a[MINH] = (tm.min / 10) & 0xf;
X	a[HOURL] = (tm.hour % 10) & 0xf;
X	a[HOURH] = ((tm.hour / 10) & 0xf) | 0x8; /* 24 hr format */
X
X	a[DAYL] = (tm.day % 10) & 0xf;
X	a[DAYH] = ((tm.day / 10) & 0xf) | (leap?4:0);
X	a[MONL] = (tm.month % 10) & 0xf;
X	a[MONH] = (tm.month / 10) & 0xf;
X	a[YEARL] = (tm.year % 10) & 0xf;
X	a[YEARH] = (tm.year / 10) & 0xf;
X  	
X	a[DAYW] = 0;
X	
X	fd = open("/dev/bms", 1);
X	if (fd < 0)
X		fatal("cannot open /dev/bms ");
X	for(n=0;n<3;n++) {
X		i = write(fd,a,N);
X		if(i == N)
X			exit(0);
X	}
X	perror("write");
X
X	exit(1);
X}
X
Xcv_time(t)
Xlong t;
X{
X  tm.year = 0;
X  tm.month = 0;
X  tm.day = 1;
X  tm.hour = 0;
X  tm.min = 0;
X  tm.sec = 0;
X  while (t >= s_p_year) {
X	if (((tm.year + 2) % 4) == 0)
X		t -= s_p_day;
X	tm.year += 1;
X	t -= s_p_year;
X  }
X  if (((tm.year + 2) % 4) == 0) {
X	days_per_month[1]++;
X	leap = 1;
X  }
X  tm.year += 1970;
X  while ( t >= (days_per_month[tm.month] * s_p_day))
X	t -= days_per_month[tm.month++] * s_p_day;
X  while (t >= s_p_day) {
X	t -= s_p_day;
X	tm.day++;
X  }
X  while (t >= s_p_hour) {
X	t -= s_p_hour;
X	tm.hour++;
X  }
X  while (t >= s_p_min) {
X	t -= s_p_min;
X	tm.min++;
X  }
X  tm.sec = (int) t;
X}
X
Xfatal(s, a1, a2, a3, a4, a5)
Xchar *s;
X{
X	printf("bmsrtc: ");
X	printf(s, a1, a2, a3, a4, a5);
X	printf(" (fatal)\n");
X	exit(1);
X}
/