[comp.os.minix] st minix 1.5 floppy-driver, misc. sector-sizes + formatting device

leo@marco.UUCP (Matthias Pfaller) (03/13/90)

This is a floppy-driver that allows you to read/write/format floppys with
sectorsizes other then 512 bytes. You also can change the number of tracks
and the number of sectors/track for each drive.
If you have connected a 10.24 Mhz oscillator to your floppy controller and
made it switchable via bit 7, sound-chip port A, you can use the higher
density for minix. I recommend 7 sectors with 1k per track.

	leo@marco.uucp (Matthias Pfaller)

PS: I would like to get some feedback (also for my patch for the st-harddisk
driver I posted recently)

echo x - fdctrl.c
sed '/^X/s///' > fdctrl.c << '/'
X#include <stdio.h>
X#include <fcntl.h>
X#include <sgtty.h>
X
Xmain(argc, argv)
Xint argc; char **argv;
X{
X	struct fd_ioctl fdio;
X	int sfd;
X	int cyl = 0, sec = 0, len = 0;
X	extern char *optarg;
X	extern int optind;
X	register char c;
X	static char usage[] = "Usage: %s [-c cylinders] [-s sectors] [-l sectorlen] raw_device\n";
X
X	while ((c = getopt(argc, argv, "c:s:l:")) != EOF)
X	{
X		switch(c)
X		{
X			case 'c':
X				cyl = atoi(optarg);
X				break;
X
X			case 's':
X				sec = atoi(optarg);
X				break;
X
X			case 'l':
X				len = atoi(optarg);
X				break;
X
X			default:
X				fprintf(stderr, usage, argv[0]);
X				exit(1);
X		}
X	}
X
X	if (!argv[optind])
X	{
X		fprintf(stderr, usage, argv[0]);
X		exit(1);
X	}
X
X	sfd = open(argv[optind], O_RDONLY);
X	if (sfd < 0)
X	{
X		perror(argv[0]);
X		exit(1);
X	}
X
X	if (ioctl(sfd, DIOCGETP, &fdio) < 0)
X	{
X		perror(argv[0]);
X		fprintf(stderr, usage, argv[0]);
X		exit(1);
X	}
X	else
X	{
X		if (cyl)
X			fdio.nr_cylinders = cyl;
X		if (sec)
X			fdio.nr_sectors = sec;
X		if (len)
X			fdio.sector_size = len;
X	}
X
X	if (ioctl(sfd, DIOCSETP, &fdio) < 0)
X	{
X		perror(argv[0]);
X		exit(1);
X	}
X
X	fprintf(stderr, "sector_size = %d nr_sectors = %d nr_cylinders = %d\n", fdio.sector_size, fdio.nr_sectors, fdio.nr_cylinders);
X	exit(0);
X}
/
echo x - flopfmt.c
sed '/^X/s///' > flopfmt.c << '/'
X#include <stdio.h>
X#include <fcntl.h>
X#include <sys/stat.h>
X#include <sgtty.h>
X
Xvoid memset(p, b, c)
Xregister char *p; register char b; register int c;
X{
X	while(c--)
X		*p++ = b;
X}
X		
Xint gap3(spt, seclen, ahdr)	/* computes the optimum size of gap3	*/
Xint spt, seclen, ahdr;		/* returns -1 for error			*/
X				/* a rotation speed variation of 2% is	*/
X{				/* asumed				*/
X	int gap3min;
X	int gap3max;
X	int gap4min;
X	int ttc;
X
X	ttc = (spt * seclen > 5120)?8000:6250;
X	gap4min = ttc / 50;
X	gap3max = (ttc - gap4min - 60 - (ahdr?22:0))/spt - (seclen + 62);
X	gap3min = (seclen + 62)/50;
X	return((gap3max < gap3min) ? -1:((gap3min + gap3max) / 2));
X}
X
Xchar *buildtrack(sectab, spt, track, side, virgin, seclen, ahdr)
Xchar *sectab; int spt, track, side, virgin, seclen, ahdr;
X{
X	int gap3len, i;
X	char *data, *malloc();
X	register char *cp;
X
X	cp = data = malloc(10000);
X	if ((gap3len = gap3(spt, seclen, ahdr)) < 0)
X		return(NULL);
X
X	/*    Track header    */
X	memset(cp, (char)0x4e, 60); cp += 60;
X
X	for(i = 0; i < spt; i++)
X	{
X		/* additional header for sector 0 */
X		if (ahdr && sectab[i] == 1)
X		{
X			/*	 sync	    */
X			memset(cp, 0x00, 12); cp += 12;
X			/* id-address  mark  */
X			memset(cp, 0xf5, 3); cp += 3;
X			*cp++ = 0xfe;
X			/*     id-field     */
X			*cp++ = track;
X			*cp++ = side;
X			*cp++ = 0;
X			*cp++ = 0;
X			/* write  CRC- code  */
X			*cp++ = 0xf7;
X		}
X
X		/*	 sync	    */
X		memset(cp, 0x00, 12); cp += 12;
X
X		/* id-address  mark  */
X		memset(cp, 0xf5, 3); cp += 3;
X		*cp++ = 0xfe;
X
X		/*     id-field     */
X		*cp++ = track;
X		*cp++ = side;
X		*cp++ = sectab[i];
X		switch(seclen)
X		{
X			case 128:
X				*cp++ = 0;
X				break;
X
X			case 256:
X				*cp++ = 1;
X				break;
X
X			case 512:
X				*cp++ = 2;
X				break;
X
X			case 1024:
X				*cp++ = 3;
X				break;
X
X			case 2048:
X				*cp++ = 4;
X				break;
X
X			case 4096:
X				*cp++ = 5;
X				break;
X
X			default:
X				exit(1);
X		}
X
X		/* write  CRC- code  */
X		*cp++ = 0xf7;
X
X		/*	 gap2	    */
X		memset(cp, 0x4e, 22); cp += 22;
X		memset(cp, 0x00, 12); cp += 12;
X
X		/* data address mark */
X		memset(cp, 0xf5, 3); cp += 3;
X		*cp++ = 0xfb;
X
X		/*    data field    */
X		memset(cp, virgin, seclen);
X		cp += seclen;
X
X		/* write  CRC- code */
X		*cp++ = 0xf7;
X
X		/*	gap 3	    */
X		memset(cp, 0x4e, gap3len); cp += gap3len;
X	}
X
X	/*	 gap 4	      */
X	memset(cp, 0x4e, 10000 - (cp - data));
X	return(data);
X}
X
Xmain(argc, argv)
Xint argc; char **argv;
X{
X	register int i, j, s, sides;
X	register char *p;
X	extern char *rindex();
X	int tfd, sfd;
X	struct fd_ioctl tfdio, sfdio;
X	struct stat statbuf;
X	char b[80];
X
X	extern char *optarg;
X	extern int optind;
X	int qflag = 0;		/* quiet mode		*/
X	int aflag = 0;		/* additional 0 header	*/
X	int interl = 1;		/* sector interleave	*/
X	int spiral = 1;		/* track interleave	*/
X	int c;
X	static char usage[] = "Usage: %s [-q] [-i sector_interleave] [-t track_interleave] raw_device\n";
X
X	char t[30], tc[30];
X
X	while ((c = getopt(argc, argv, "qai:t:")) != EOF)
X	{
X		switch(c)
X		{
X			case 'q':
X				qflag++;
X				break;
X
X			case 'a':
X				aflag++;
X				break;
X
X			case 'i':
X				interl = atoi(optarg);
X				break;
X
X			case 't':
X				spiral = atoi(optarg);
X				break;
X
X			default:
X				fprintf(stderr, usage, argv[0]);
X				exit(1);
X		}
X	}
X
X	if (!argv[optind])
X	{
X		fprintf(stderr, usage, argv[0]);
X		exit(1);
X	}
X
X	sfd = open(argv[optind], O_RDONLY);
X	if (sfd < 0)
X	{
X		perror(argv[0]);
X		exit(1);
X	}
X	p = rindex(argv[optind], '/');
X	p = p?p + 1:argv[optind];
X	*p = 't';
X	tfd = open(argv[optind], O_WRONLY);
X	if (tfd < 0)
X	{
X		perror(argv[0]);
X		exit(1);
X	}
X	if (ioctl(tfd, DIOCGETP, &tfdio) < 0 || ioctl(sfd, DIOCGETP, &sfdio) < 0)
X	{
X		perror(argv[0]);
X		exit(1);
X	}
X	if (fstat(tfd, &statbuf) < 0)
X	{
X		perror(argv[0]);
X		exit(1);
X	}
X	if (!(statbuf.st_rdev & 0x20))
X	{
X		fprintf(stderr, "%s: no formatting device (devno = %d)\n", argv[0], statbuf.st_rdev);
X		exit(1);
X	}
X	sides = statbuf.st_rdev & 0x8?2:1;
X
X	for (i = 0; i < sfdio.nr_sectors; i++)
X		t[i] = -1;
X
X	for (i = 1, j = 0; i <= sfdio.nr_sectors; i++)
X	{
X		while (t[j] != -1)
X			j = (j+1) % sfdio.nr_sectors;
X
X		t[j] = i;
X		j = (j+interl) % sfdio.nr_sectors;
X	}
X
X	if (!qflag)
X	{
X		printf("Sector sequence: ");
X		for (i = 0; i < sfdio.nr_sectors; i++)
X			printf(" %2d ", t[i]);
X		printf("\n");
X	}
X
X	for (i = 0; i < sfdio.nr_cylinders; i++)
X	{
X		for (j = 0; j < sides; j++)
X		{
X			p = buildtrack(t, sfdio.nr_sectors, i, j, 0xe5, sfdio.sector_size, aflag);
X			if (!p)
X			{
X				fprintf(stderr, "\n%s: track too long\n", argv[0]);
X				exit(1);
X			}
X
X			if (!qflag)
X				printf("Formatting track %02d side %d\r", i, j);
X
X			if (write(tfd, p, tfdio.sector_size) < 0)
X			{
X				fprintf(stderr, "\n");
X				perror(argv[0]);
X				exit(1);
X			}
X			free(p);
X		}
X
X		for (j = 0; j < sfdio.nr_sectors; j++)
X			tc[(j + spiral)%sfdio.nr_sectors] = t[j];
X
X		for (j = 0; j < sfdio.nr_sectors; j++)
X			t[j] = tc[j];
X	}
X	if (!qflag)
X		printf("\n");
X	exit(0);
X}
X
/
echo x - ioctl.c
sed '/^X/s///' > ioctl.c << '/'
X#include <lib.h>
X#include <minix/com.h>
X#include <sgtty.h>
X
XPUBLIC int ioctl(fd, request, argp)
Xint fd;
Xint request;
Xstruct sgttyb *argp;
X{
X  int n;
X  long erase, kill, intr, quit, xon, xoff, eof, brk, speed;
X  struct tchars *argt;
X
X  M.TTY_REQUEST = request;
X  M.TTY_LINE = fd;
X
X  switch(request) {
X     case TIOCSETP:
X	erase = argp->sg_erase & BYTE;
X	kill = argp->sg_kill & BYTE;
X	speed = ((argp->sg_ospeed & BYTE) << 8) | (argp->sg_ispeed & BYTE);
X	M.TTY_SPEK = (speed << 16) | (erase << 8) | kill;
X	M.TTY_FLAGS = argp->sg_flags;
X	n = callx(FS, IOCTL);
X  	return(n);
X 
X     case DIOCSETP:
X     case TIOCSETC:
X	argt = (struct tchars * /* kludge */) argp;
X  	intr = argt->t_intrc & BYTE;
X  	quit = argt->t_quitc & BYTE;
X  	xon  = argt->t_startc & BYTE;
X  	xoff = argt->t_stopc & BYTE;
X  	eof  = argt->t_eofc & BYTE;
X  	brk  = argt->t_brkc & BYTE;		/* not used at the moment */
X  	M.TTY_SPEK = (intr<<24) | (quit<<16) | (xon<<8) | (xoff<<0);
X  	M.TTY_FLAGS = (eof<<8) | (brk<<0);
X  	n = callx(FS, IOCTL);
X  	return(n);
X  	
X     case TIOCGETP:
X  	n = callx(FS, IOCTL);
X	argp->sg_erase = (M.TTY_SPEK >> 8) & BYTE;
X	argp->sg_kill  = (M.TTY_SPEK >> 0) & BYTE;
X  	argp->sg_flags = M.TTY_FLAGS & 0xFFFFL;
X	speed = (M.TTY_SPEK >> 16) & 0xFFFFL;
X	argp->sg_ispeed = speed & BYTE;
X	argp->sg_ospeed = (speed >> 8) & BYTE;
X  	return(n);
X
X     case DIOCGETP:
X     case TIOCGETC:
X  	n = callx(FS, IOCTL);
X	argt = (struct tchars *) argp;
X  	argt->t_intrc  = (M.TTY_SPEK >> 24) & BYTE;
X  	argt->t_quitc  = (M.TTY_SPEK >> 16) & BYTE;
X  	argt->t_startc = (M.TTY_SPEK >>  8) & BYTE;
X  	argt->t_stopc  = (M.TTY_SPEK >>  0) & BYTE;
X  	argt->t_eofc   = (M.TTY_FLAGS >> 8) & BYTE;
X  	argt->t_brkc   = (M.TTY_FLAGS >> 8) & BYTE;
X  	return(n);
X
X/* This is silly, do we want to add 1001 cases and M.TTY_XYZ's here?
X * We should just pop argp into the message for low-level interpretation.
X */
X
X     case TIOCFLUSH:
X	M.TTY_FLAGS = (int /* kludge */) argp;
X	return callx(FS, IOCTL);
X
X     default:
X	n = -1;
X	errno = -(EINVAL);
X	return(n);
X  }
X}
/
echo x - stfloppy.c
sed '/^X/s///' > stfloppy.c << '/'
X#if (MACHINE == ATARI)
X/*
X * This file contains a driver for the Floppy Disk Controller (FDC)
X * on the Atari ST. It uses the WD 1772 chip, modified for steprates.
X * The driver supports two operations: read a block and write a block.
X * It accepts four messages, for reading, for writing, for scattered
X * I/O and for control. All use message format m2 with these
X * parameters:
X *
X *    m_type      DEVICE    PROC_NR     COUNT    POSITION  ADRRESS
X * ----------------------------------------------------------------
X * |  DISK_READ | device  | proc nr |  bytes  |  offset | buf ptr |
X * |------------+---------+---------+---------+---------+---------|
X * | DISK_WRITE | device  | proc nr |  bytes  |  offset | buf ptr |
X * |------------+---------+---------+---------+---------+---------|
X * |SCATTERED_IO| device  | proc nr | requests|         | iov ptr |
X * |------------+---------+---------+---------+---------+---------|
X * | DISK_IOCTL | device  | proc nr |func code| spt etc |         |
X * ----------------------------------------------------------------
X *
X * The file contains two entry points:
X *
X *	floppy_task:	main entry when system is brought up
X *	fd_timer:	used to deselect drives, called from clock.c
X *
X * The ST floppy disk controller shares the access to the DMA circuitry
X * with other devices. For this reason the floppy disk controller makes
X * use of some special DMA accessing code.
X *
X * Interrupts from the FDC are in fact DMA interrupts which get their
X * first level handling in stdma.c . If the floppy driver is currently
X * using DMA the interrupt is signalled to the floppy task.
X */
X
X#include "kernel.h"
X#include <minix/callnr.h>
X#include <minix/com.h>
X#include <sgtty.h>
X#include "proc.h"
X
X#include "staddr.h"
X#include "stfdc.h"
X#include "stdma.h"
X#include "stsound.h"
X
X#define	ASSERT(x)	if (!(x)) panic("fd: ASSERT(x) failed", NO_NUM);
X#define TRACE(x)	/* x */
X#define DEBUG(x)	x
X
X#define	FDC_DELAY	32	/* for dma[rw]dat() */
X
X/* Parameters for the disk drive. */
X#define D_SECTOR_SIZE	512	/* physical sector size in bytes */
X#define D_NR_SECTORS	10	/* number of sectors per track */
X#define D_NR_CYLINDERS	82	/* number of cylinders */
X#define NR_DRIVES	2	/* maximum number of drives */
X#define NR_TYPES	8	/* number of diskette/drive combinations */
X#define MAX_ERRORS	10	/* how often to try rd/wt before quitting */
X#define SIDES		1	/* sides-mask in drive type */
X#define DENSE		2	/* hd/nd mask in drive type */
X#define FORMAT		4	/* format-mask in drive type */
X
X/*
X * The minor device number is build as follows
X *     00fhd00n
X *	f = 1 -> formatting device
X *	h = 1 -> high density (320 kbit) device
X *	d = 1 -> double sided device
X *	n = 0 -> select drive 0
X *	n = 1 -> select drive 1
X * The index into the fparam table is computed by
X * ((DTYPE(minor) << 1)|DRIVE(minor))
X *
X * All parameters in fparam can be set by an ioctl-system call.
X * The nr_cylinders-field of the format device is allways set
X * to the value of the corresponding non-format device.
X * The parameters of format devices are read-only.
X */
X
Xstruct fparam {			/* variable parameters for each minor device */
X	int sector_size;
X	char nr_sectors;
X	char nr_cylinders;
X} fparam[16] = {
X	{ D_SECTOR_SIZE, D_NR_SECTORS, D_NR_CYLINDERS },
X	{ D_SECTOR_SIZE, D_NR_SECTORS, D_NR_CYLINDERS },
X	{ D_SECTOR_SIZE, D_NR_SECTORS, D_NR_CYLINDERS },
X	{ D_SECTOR_SIZE, D_NR_SECTORS, D_NR_CYLINDERS },
X	{ 1024, 7, D_NR_CYLINDERS },
X	{ 1024, 7, D_NR_CYLINDERS },
X	{ 1024, 7, D_NR_CYLINDERS },
X	{ 1024, 7, D_NR_CYLINDERS },
X	{ 6656, 1, D_NR_CYLINDERS },
X	{ 6656, 1, D_NR_CYLINDERS },
X	{ 6656, 1, D_NR_CYLINDERS },
X	{ 6656, 1, D_NR_CYLINDERS },
X	{ 8192, 1, D_NR_CYLINDERS },
X	{ 8192, 1, D_NR_CYLINDERS },
X	{ 8192, 1, D_NR_CYLINDERS },
X	{ 8192, 1, D_NR_CYLINDERS }
X};
X
X/* return values of xfer_ok(): */
X#define X_OK		0
X#define X_AGAIN		1
X#define X_ERROR		2
X#define X_FAIL		3
X
XPRIVATE int curcyl[NR_DRIVES];	/* current cylinder (-1 if not calibrated) */
X
XPRIVATE message mess;		/* message buffer for in and out */
X
XPRIVATE struct xfer {
X	int	x_rw;		/* read or write */
X	int	x_drive;	/* drive number */
X	int	x_secnum;	/* current sector */
X	phys_bytes x_address;	/* current physical address */
X	int	x_count;	/* bytes still to transfer */
X	int	x_errors;	/* errors on current sector */
X	int	x_cmd;		/* controller command */
X} xfer;
X
X#define	DRIVE(d)	((d) & 0x07)
X#define	DTYPE(d)	(((d) >> 3) & 0x1F)
X
Xstatic int do_open();
Xstatic int do_rdwt();
Xstatic int do_xfer();
Xstatic int select();
Xstatic void deselect();
Xstatic void fdcint();
Xstatic int xfer_done();
Xstatic int xfer_ok();
X
X/*===========================================================================*
X *				floppy_task				     *
X *===========================================================================*/
XPUBLIC void floppy_task()
X{
X  register r, drive, caller, procno;
X
X  /*
X   * The main loop of the disk task.
X   * It waits for a message, carries it out, and sends a reply.
X   */
X  TRACE(printf("fd: task started\n"));
X
X  dmagrab(FLOPPY, fdcint);
X  dmawdat(FDC_CS, IRUPT, FDC_DELAY);		/* reset controller */
X  select(0, 0, 0);
X  deselect();
X  dmafree(FLOPPY);
X
X  for (drive = 0; drive < NR_DRIVES; drive++)
X	curcyl[drive] = -1;	/* uncalibrated */
X  while (TRUE) {
X	receive(ANY, &mess);
X	ASSERT(mess.m_source >= 0);
X	TRACE(printf("fd: 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 DISK_READ:
X	    case DISK_WRITE:	r = do_rdwt(&mess);	break;
X	    case SCATTERED_IO:	r = do_vrdwt(&mess, do_rdwt); break;
X	    case DISK_IOCTL:	r = do_ioctl(&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 *				do_rdwt					     *
X *===========================================================================*/
XPRIVATE int do_rdwt(mp)
Xregister message *mp;
X{
X  register struct xfer *xp;
X  register struct fparam *f;
X  register struct proc *rp;
X  register nbytes;
X
X  xp = &xfer;
X  xp->x_rw = mp->m_type;
X  xp->x_drive = mp->DEVICE;
X  if (DRIVE(xp->x_drive) >= NR_DRIVES)
X	return(EIO);
X  if (DTYPE(xp->x_drive) >= NR_TYPES)
X	return(EIO);
X  if ((DTYPE(xp->x_drive) & FORMAT) && xp->x_rw == DISK_READ)
X  	return(EINVAL);
X
X  f = fparam + (DTYPE(xp->x_drive)<<1) + DRIVE(xp->x_drive);
X  nbytes = mp->COUNT;
X  xp->x_count = nbytes;
X
X  if ((mp->POSITION % f->sector_size) != 0)
X	return(EINVAL);
X  if ((nbytes % f->sector_size) != 0)
X	return(EINVAL);
X  xp->x_secnum = (int)(mp->POSITION/f->sector_size);
X  rp = proc_addr(mp->PROC_NR);
X  xp->x_address = umap(rp, D, (vir_bytes) mp->ADDRESS, (vir_bytes) nbytes);
X  if (xp->x_address == 0)
X	return(EINVAL);
X  if (nbytes == 0)
X	return(0);
X  rp->p_physio = 1;		/* disable (un)shadowing */
X  dmagrab(FLOPPY, fdcint);
X  xp->x_errors = 0;
X  if (do_xfer()) {
X	receive(HARDWARE, &mess);
X	deselect();
X  }
X  dmafree(FLOPPY);
X  rp->p_physio = 0;		/* enable (un)shadowing */
X  if (xp->x_count == nbytes)
X	return(0);		/* nothing transferred */
X  if (xp->x_count != 0)
X	return(EIO);		/* partial transfer */
X  return(nbytes);
X}
X
X/*===========================================================================*
X *				do_xfer					     *
X *===========================================================================*/
XPRIVATE int do_xfer()
X{
X  register struct xfer *xp;
X  register struct fparam *f;
X  register d, head, cylinder, sector, hbit, dense;
X  static int ebit = 0;
X
X  xp = &xfer;
X
X  d = DTYPE(xp->x_drive);
X  f = fparam + (d<<1) + DRIVE(xp->x_drive);
X  cylinder = xp->x_secnum / f->nr_sectors;
X  head = (d & SIDES)?cylinder & 1:0;
X  cylinder = (d & SIDES)?cylinder >> 1:cylinder;
X  if (cylinder >= f->nr_cylinders)
X	return(0);
X
X  dense = d & DENSE;
X  xp->x_cmd = (xp->x_rw == DISK_READ)?F_READ:(d & FORMAT?WRITETR:F_WRITE);
X  d = DRIVE(xp->x_drive);
X
X  hbit = 0;
X  if (select(d, head, dense) != 0)
X	hbit = HBIT;	/* motor on, suppress spin up sequence */
X
X  if (curcyl[d] == -1) {
X	/* 
X	 * Recalibrate, since we lost track of head positioning.
X	 * The floppy disk controller has no way of determining its
X	 * absolute arm position (cylinder).  Instead, it steps the
X	 * arm a cylinder at a time and keeps track of where it
X	 * thinks it is (in software).  However, after a SEEK, the
X	 * hardware reads information from the diskette telling
X	 * where the arm actually is.  If the arm is in the wrong place,
X	 * a recalibration is done, which forces the arm to cylinder 0.
X	 * This way the controller can get back into sync with reality.
X	 */
X	TRACE(printf("fd%d: recalibrate\n", xp->x_drive));
X	xp->x_cmd = RESTORE;
X	dmawdat(FDC_CS, RESTORE|(dense?RATE6:RATE3)|hbit, FDC_DELAY);
X	ebit = EBIT;
X	return(1);
X  }
X
X  dmawdat(FDC_TR, curcyl[d], FDC_DELAY);
X
X  /*
X   * Issue a SEEK command on the indicated drive unless the arm is
X   * already positioned on the correct cylinder.
X   */
X  if (cylinder != curcyl[d]) {
X	curcyl[d] = cylinder;	/* be optimistic */
X	xp->x_cmd = SEEK;
X	dmawdat(FDC_DR, cylinder, FDC_DELAY);
X	dmawdat(FDC_CS, SEEK|(dense?RATE6:RATE3)|hbit, FDC_DELAY);
X	ebit = EBIT;
X	return(1);
X  }
X
X  /* The drive is now on the proper cylinder.  Read or write 1 block. */
X  sector = xp->x_secnum % f->nr_sectors;
X  sector++;	/* start numbering at 1 */
X
X  TRACE(printf("fd%d: %s: secnum=%d,cylinder=%d,sector=%d,head=%d\n",
X	xp->x_drive, xp->x_rw == DISK_READ ? "read" : "write",
X	xp->x_secnum, cylinder, sector, head));
X
X  dmawdat(FDC_SR, sector, FDC_DELAY);
X  dmaaddr(xp->x_address);	/* DMA address setup */
X
X  if (xp->x_rw == DISK_READ) {
X	/* Issue the command */
X	dmacomm(FDC | SCREG, f->sector_size / 512, 0);
X	dmawdat(FDC_CS, xp->x_cmd|hbit|ebit, FDC_DELAY);
X  } else {
X	/* Issue the command */
X	dmacomm(WRBIT | FDC | SCREG, f->sector_size / 512, FDC_DELAY);
X	dmawdat(WRBIT | FDC_CS, xp->x_cmd|hbit|ebit, FDC_DELAY);
X  }
X  ebit = 0;
X  return(1);
X}
X
X/*===========================================================================*
X *			select,	deselect, fd_timer			     *
X *===========================================================================*/
X
XPRIVATE int selected;		/* drive/head currently selected */
XPRIVATE int selectic = 30;	/* # of fd_timer calls to deselect */
XPRIVATE int motoron;		/* for how long will motor run? */
X
XPRIVATE int select(drive, head, dense)
Xint drive;
Xint head;
Xint dense;
X{
X  register i, s, spinning;
X
X  TRACE(printf("fd: select (drive %d, head %d)\n", drive, head));
X  spinning = motoron;
X  motoron = 5;		/* will run for more than 500 msec */
X  selectic = -30;	/* error if not ready within 3 sec */
X  i = (drive == 1 ? PA_FLOP1 : PA_FLOP0);
X  i |= head;
X  if (i != selected) {
X	selected = i;
X	s = lock();
X	SOUND->sd_selr = YM_IOA;
X	SOUND->sd_wdat = (SOUND->sd_rdat & 0x78) | (i ^ 0x07) | (dense?PA_FDDENS:0);
X	restore(s);
X  }
X  return(spinning);
X}
X
XPRIVATE void deselect()
X{
X  TRACE(printf("fd: deselect\n"));
X  selectic = 30;	/* postpone for 30 * 100 msec == 3 sec */
X}
X
XPUBLIC void fd_timer()
X{
X/*
X * Perform three functions:
X * 1. Estimate when motor stops running
X * 2. Force interrupt if drive does not respond
X * 3. Deselect drive(s) when all motors stopped spinning
X */
X  register s, wrbit;
X
X  if (selectic == 0)
X	return;
X  if (motoron)
X	--motoron;
X  if (selectic < 0) {
X	if (++selectic == 0) {
X		s = lock();
X		wrbit = (xfer.x_cmd == F_WRITE || xfer.x_cmd == WRITETR) ?
X		        WRBIT : 0;
X		xfer.x_cmd = IRUPT;
X		dmawdat(FDC_CS, wrbit|IRUPT, FDC_DELAY);
X		fdcint();
X		restore(s);
X	}
X	return;
X  }
X  if (--selectic != 0)
X	return;
X  selected = 0;
X  s = lock();
X  SOUND->sd_selr = YM_IOA;
X  SOUND->sd_wdat = SOUND->sd_rdat | 0x07;
X  restore(s);
X}
X
X/*===========================================================================*
X *				fdcint					     *
X *===========================================================================*/
XPRIVATE void fdcint()
X{
X  ASSERT(xfer.x_count)
X  if (xfer_done() == 0)
X	return;
X  interrupt(FLOPPY);
X}
X
X/*===========================================================================*
X *				xfer_done				     *
X *===========================================================================*/
XPRIVATE int xfer_done()
X{
X  register struct xfer *xp;
X  register struct fparam *f;
X
X  xp = &xfer;
X  f = fparam + (DTYPE(xp->x_drive)<<1) + DRIVE(xp->x_drive);
X  switch (xfer_ok()) {
X  case X_FAIL:
X	return(1);
X  case X_ERROR:
X	xp->x_errors++;
X	if (xp->x_errors == MAX_ERRORS)
X		return(1);
X	break;
X  case X_AGAIN:
X	break;
X  case X_OK:
X	xp->x_errors = 0;
X	xp->x_secnum++;
X	xp->x_address += f->sector_size;
X	xp->x_count -= f->sector_size;
X	if (xp->x_count == 0)
X		return(1);
X	break;
X  }
X  if (do_xfer() == 0)
X	return(1);
X  return(0);
X}
X
X/*===========================================================================*
X *				xfer_ok					     *
X *===========================================================================*/
XPRIVATE int xfer_ok()
X{
X  register struct xfer *xp;
X  register status;
X
X  xp = &xfer;
X  switch (xp->x_cmd) {
X  case IRUPT:
X	printf("fd%d: timeout\n", xp->x_drive);
X	curcyl[DRIVE(xp->x_drive)] = -1;
X	return(X_ERROR);
X  case F_READ:
X	/* Test for DMA error */
X	status = dmastat(FDC_CS | SCREG, 0);
X	if ((status & DMAOK) == 0) {
X		printf("fd%d: read: dma status = 0x%x\n",
X			xp->x_drive, DMA->dma_stat & BYTE);
X		return(X_ERROR);
X	}
X	/* Get controller status and check for errors. */
X	status = dmardat(FDC_CS, FDC_DELAY);
X	if (status & (RNF | CRCERR | LD_T00)) {
X		printf("fd%d: read sector %d: fdc status = 0x%x\n",
X			xp->x_drive, xp->x_secnum, status&0xFF);
X		if (xp->x_errors > 2)
X			curcyl[DRIVE(xp->x_drive)] = -1;
X		dmawdat(FDC_CS, IRUPT, FDC_DELAY);	/* reset controller */
X		return(X_ERROR);
X	}
X	break;
X  case F_WRITE:
X  case WRITETR:
X	/* Test for DMA error */
X	status = dmastat(FDC_CS | SCREG, 0);
X	if ((status & DMAOK) == 0) {
X		printf("fd%d: write: dma status = 0x%x\n",
X			xp->x_drive, DMA->dma_stat & BYTE);
X		dmawdat(FDC_CS, IRUPT, FDC_DELAY);	/* reset controller */
X		return(X_ERROR);
X	}
X	/* Get controller status and check for errors. */
X	status = dmardat(WRBIT | FDC_CS, FDC_DELAY);
X	if (status & WRI_PRO) {
X		printf("fd%d: write protected\n", xp->x_drive);
X		return(X_FAIL);
X	}
X	if (status & (RNF | CRCERR | LD_T00)) {
X		printf("fd%d: write sector %d: fdc status = 0x%x\n",
X			xp->x_drive, xp->x_secnum, status&0xFF);
X		if (xp->x_errors > 2)
X			curcyl[DRIVE(xp->x_drive)] = -1;
X		return(X_ERROR);
X	}
X	break;
X  case SEEK:
X	status = dmardat(FDC_CS, FDC_DELAY);
X	if (status & (RNF | CRCERR)) {
X		curcyl[DRIVE(xp->x_drive)] = -1;
X		return(X_ERROR);
X	}
X	return(X_AGAIN);
X  case RESTORE:
X	/* Determine if the recalibration succeeded. */
X	status = dmardat(FDC_CS, FDC_DELAY);
X	if (status & RNF) {
X		printf("fd%d: recalibrate failed. status = 0x%x\n",
X				xp->x_drive, status&0xFF);
X		dmawdat(FDC_CS, IRUPT, FDC_DELAY);	/* reset controller */
X		curcyl[DRIVE(xp->x_drive)] = -1;
X		return(X_ERROR);
X	}
X	curcyl[DRIVE(xp->x_drive)] = 0;
X	return(X_AGAIN);
X  default:
X	ASSERT(0);
X  }
X  return(X_OK);
X}
X
X/*===========================================================================*
X *				do_ioctl				     *
X *===========================================================================*/
XPRIVATE int do_ioctl(mp)
Xregister message *mp;		/* pointer to read or write message */
X{
X  register struct fparam *f;
X  register int sector_size;
X  register unsigned char nr_sectors, nr_cylinders;
X
X  if (DRIVE(mp->DEVICE) >= NR_DRIVES)
X	return(ENXIO);
X  if (DTYPE(mp->DEVICE) >= NR_TYPES)
X	return(ENXIO);
X
X  f = fparam + (DTYPE(mp->DEVICE)<<1) + DRIVE(mp->DEVICE);
X  switch(mp->COUNT)
X  {
X	case DIOCGETP:
X		mp->POSITION = ((long)f->sector_size << 16) | (f->nr_sectors << 8) | f->nr_cylinders;
X		break;
X
X	case DIOCSETP:
X		if (DTYPE(mp->DEVICE) & FORMAT)
X			return(ENXIO);
X		sector_size = mp->POSITION >> 16;
X		nr_sectors = (mp->POSITION >> 8) & BYTE;
X		nr_cylinders = mp->POSITION & BYTE;
X		if (sector_size !=  512 && sector_size != 1024 &&
X		    sector_size != 2048 && sector_size != 4096)
X			return(EINVAL);
X		if (nr_sectors * sector_size > 7186)
X			return(EINVAL);
X		if (!(DTYPE(mp->DEVICE) & DENSE)
X		    && nr_sectors * sector_size > 5120)
X			return(EINVAL);
X		if (nr_cylinders >84)
X			return(EINVAL);
X		f->sector_size = sector_size;
X		f->nr_sectors = nr_sectors;
X		f->nr_cylinders = nr_cylinders;
X		f += (FORMAT << 1);
X		f->nr_cylinders = nr_cylinders;
X		break;
X
X	default:
X		return(EINVAL);
X  }
X  return(0);
X}
X#endif
/
echo x - stsound.cdif
sed '/^X/s///' > stsound.cdif << '/'
X*** stsound.h.1.5	Mon Mar 12 16:53:45 1990
X--- stsound.h	Mon Mar 12 16:55:15 1990
X***************
X*** 62,65 ****
X  #define	PA_SDTR		0x10	/* Serial DTR */
X  #define	PA_PSTROBE	0x20	/* Parallel Strobe */
X  #define	PA_USER		0x40	/* Free Pin on Monitor Connector */
X! /*			0x80	/* reserved */
X--- 62,65 ----
X  #define	PA_SDTR		0x10	/* Serial DTR */
X  #define	PA_PSTROBE	0x20	/* Parallel Strobe */
X  #define	PA_USER		0x40	/* Free Pin on Monitor Connector */
X! #define PA_FDDENS	0x80	/* select floppy disk density */
/

leo@marco.UUCP (Matthias Pfaller) (03/13/90)

sorry, i forgot to post the patch for sgtty.h

	leo@marco.UUCP (Matthias Pfaller)

*** sgtty.h.1.5	Mon Mar 12 17:12:26 1990
--- sgtty.h	Mon Mar 12 17:11:46 1990
***************
*** 34,40 ****
--- 34,43 ----
  #define CBREAK	     0000002	/* enable cbreak mode */
  #define COOKED       0000000	/* neither CBREAK nor RAW */
  
+ #define DCD	     010000	/* Data Carrier Detect */
+ 
  /* Line speeds */
+ #define B0		   0	/* code for line-hangup */
  #define B110		   1
  #define B300		   3
  #define B1200		  12
***************
*** 83,88 ****
--- 86,102 ----
  #define	DC_RICD  	(DCLOCK|4)
  #define	DC_WBMS100	(DCLOCK|8)
  #define	DC_WBMS200	(DCLOCK|9)
+ 
+ /* Data structures for disk ioctl */
+ 
+ struct fd_ioctl {
+ 	int sector_size;	/* sector size in bytes */
+ 	char nr_sectors;	/* sectors per track */
+ 	char nr_cylinders;	/* number of cylinders */
+ };
+ 
+ #define DIOCGETP (('d'<<8)|8)
+ #define DIOCSETP (('d'<<8)|9)
  #endif
  
  #include <ansi.h>

leo@marco.UUCP (Matthias Pfaller) (03/16/90)

please change the definition of fd_ioctl in sgtty.h from
struct fd_ioctl {
    int sector_size;	    /* sector size in bytes */
    char nr_sectors;	    /* sectors per track */
    char nr_cylinders;	    /* number of cylinders */
};
to
struct fd_ioctl {
    int sector_size;	    /* sector size in bytes */
    char nr_sectors;	    /* sectors per track */
    char nr_cylinders;	    /* number of cylinders */
    int filler;
};
The formatter will not work without this change.
	leo@marco.UUCP (Matthias Pfaller)