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}
/