/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* bos720 src/bos/usr/samples/fd/fd.c 1.1                                 */
/*                                                                        */
/* Licensed Materials - Property of IBM                                   */
/*                                                                        */
/* COPYRIGHT International Business Machines Corp. 1988,1993              */
/* All Rights Reserved                                                    */
/*                                                                        */
/* US Government Users Restricted Rights - Use, duplication or            */
/* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.      */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
static char sccsid[] = "@(#)65	1.1  src/bos/usr/samples/fd/fd.c, fdsamp, bos720 10/12/93 14:35:44";
/*
 *   COMPONENT_NAME: FDSAMP (Diskette Device Driver)
 *
 *   FUNCTIONS: fd_config, fd_open, fd_close, fd_read, fd_write,
 *              fd_ioctl, fdopen_exit
 *
 *   INTERNAL FUNCTIONS: fdcleanup, fdio, fdload_floppy, fdnull
 *
 *
 *   ORIGINS: 27
 *
 *
 *   (C) COPYRIGHT International Business Machines Corp. 1988,1993
 *   All Rights Reserved
 *   Licensed Materials - Property of IBM
 *   US Government Users Restricted Rights - Use, duplication or
 *   disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 */

/*
	    NOTICE TO USERS OF THE SOURCE CODE EXAMPLES

 THE SOURCE CODE EXAMPLES PROVIDED BY IBM ARE ONLY INTENDED TO ASSIST IN THE
 DEVELOPMENT OF A WORKING SOFTWARE PROGRAM.  THE SOURCE CODE EXAMPLES DO NOT
 FUNCTION AS WRITTEN:  ADDITIONAL CODE IS REQUIRED.  IN ADDITION, THE SOURCE
 CODE EXAMPLES MAY NOT COMPILE AND/OR BIND SUCCESSFULLY AS WRITTEN.
 
 INTERNATIONAL BUSINESS MACHINES CORPORATION PROVIDES THE SOURCE CODE
 EXAMPLES, BOTH INDIVIDUALLY AND AS ONE OR MORE GROUPS, "AS IS" WITHOUT
 WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT
 LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
 OF THE SOURCE CODE EXAMPLES, BOTH INDIVIDUALLY AND AS ONE OR MORE GROUPS,
 IS WITH YOU.  SHOULD ANY PART OF THE SOURCE CODE EXAMPLES PROVE
 DEFECTIVE, YOU (AND NOT IBM OR AN AUTHORIZED RISC System/6000* WORKSTATION
 DEALER) ASSUME THE ENTIRE COST OF ALL NECESSARY SERVICING, REPAIR OR
 CORRECTION.

 IBM does not warrant that the contents of the source code examples, whether
 individually or as one or more groups, will meet your requirements or that
 the source code examples are error-free.

 IBM may make improvements and/or changes in the source code examples at
 any time.

 Changes may be made periodically to the information in the source code
 examples; these changes may be reported, for the sample device drivers
 included herein, in new editions of the examples.

 References in the source code examples to IBM products, programs, or
 services do not imply that IBM intends to make these available in all
 countries in which IBM operates.  Any reference to an IBM licensed
 program in the source code examples is not intended to state or imply
 that only IBM's licensed program may be used.  Any functionally equivalent
 program may be used.

 * RISC System/6000 is a trademark of International Business Machines 
   Corporation.
*/

/*
 * FUNCTION: This device driver provides device support for the
 *           diskette controller and its attached drives.  Support
 *           is provided for one adapter and up to two drives.
 *
 */

#include <fcntl.h>
#include <unistd.h>
#include <sys/adspace.h>
#include <sys/buf.h>
#include <sys/device.h>
#include <sys/devinfo.h>
#include <sys/dma.h>
#include <sys/errids.h>
#include <sys/errno.h>
#include <sys/except.h>
#include <sys/fd.h>
#include <sys/intr.h>
#include <sys/ioacc.h>
#include <sys/ioctl.h>
#include <sys/lockl.h>
#include <sys/malloc.h>
#include <sys/param.h>
#include <sys/pin.h>
#include <sys/pri.h>
#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/sleep.h>
#include <sys/sysmacros.h>
#include <sys/syspest.h>
#include <sys/time.h>
#include <sys/timer.h>
#include <sys/trchkid.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/watchdog.h>

#include "fd_local.h"

/* All globals start with 'fd' */

uchar fdglobal_init = FALSE;	/* allocate and initialize the global
						initialization flag */

/*
 * allocate and initialize the lock word.
 */

lock_t fd_lock = LOCK_AVAIL;

extern	struct adapter_structure *fdadapter;

extern	struct fdconfig_parameters fdconfparms[FDMAXDRIVES];




/*
 * NAME: fd_config
 *
 * FUNCTION: fd_config() initializes the diskette device driver.
 *  It will take data from a structure of type 'fd_config' pointed
 *  to by the uio structure and store it in a local structure
 *  of type 'floppy'.
 *
 * EXECUTION ENVIRONMENT: process level
 *
 * NOTES:
 *
 * INPUTS:
 *  devno - the major and minor device numbers.
 *  cmd   - CFG_INIT if device is being defined.
 *        - CFG_TERM if device is being deleted.
 *  uiop  - pointer to uio structure containing fd_config data.
 *
 * OUTPUTS: none.
 *
 * EXTERNAL PROCEDURES CALLED:
 *  uiomove, lockl, unlockl
 *
 * ERROR CODES: The following errno values may be returned:
 *  EBUSY   - drive is not in the closed state.
 *  ENXIO   - invalid minor number.
 *  ENINVAL - invalid cmd argument.
 */

int	fd_config (dev_t devno, int cmd, register struct uio *uiop)
{
	struct	fd_config configstruct;
	struct	fd_vpd	vpdstruct;
	struct	uio	localuio;
	struct	iovec	*localuio_iovec;
	register struct fd_config *configptr;
	register struct fd_vpd *vpdptr;
	register struct floppy *fdp;
	struct	devsw fddevsw;
	int	drive_number, i, rc = FDSUCCESS, trc;
	caddr_t	iocc_val;
	uchar	temp_val, delete_adapter;

	lockl(&fd_lock, LOCK_SHORT);

	/*
	 * Use minor device number to determine which drive to use.
	 */

	drive_number = minor(devno) >> 6;
	if (drive_number < 0 || drive_number >= FDMAXDRIVES) {
		unlockl(&fd_lock);
		return(ENXIO);
	}

	/*
	 * The drive number is valid.
	 */

	switch (cmd) {
	case CFG_TERM:

		/*
		 * If the adapter structure has never been initialized, then
		 * there is nothing to delete.
		 */

		if (fdglobal_init == FALSE) {
			unlockl(&fd_lock);
			return(ENXIO);
		}

		/*
		 * If the floppy structure has never been allocated for this
		 * drive, then there is nothing to delete.
		 */

		fdp = fdadapter->drive_list[drive_number];
		if (fdp == NULL) {
			unlockl(&fd_lock);
			return(ENXIO);
		}

		/*     
		 * If drive is not closed, return with error.
		 */

		if (fdp->drive_state != FDCLOSED) {
			unlockl(&fd_lock);
			return(EBUSY);
		}

		/*     
		 * Otherwise delete the drive.
		 */

		rc = free((caddr_t)fdp);
		fdadapter->drive_list[drive_number] = NULL;
		delete_adapter = TRUE;
		for (i = 0; i < FDMAXDRIVES; i++) {
			if (fdadapter->drive_list[i] != NULL) {
				delete_adapter = FALSE;
				break;
			}
		}
		if (delete_adapter) {

			/* 
			 * If no other drives are defined
			 * delete from device switch table,
			 * free adapter structure, and clear
			 * global initialization flag.
			 */

			rc = devswdel(devno);
			if (rc != 0) {
				unlockl(&fd_lock);
				return(rc);
			}
			rc = free((caddr_t)fdadapter);
			fdglobal_init = FALSE;
		}
		break; /* end CFG_TERM case */
	case CFG_INIT:

		if (fdglobal_init == FALSE) {

			/*
		 	 * Allocate the adapter structure and initialize
			 * global parameters.
		 	 */

			fdadapter = (struct adapter_structure *)
			    malloc(sizeof(struct adapter_structure ));
			if (fdadapter == NULL) {
				unlockl(&fd_lock);
				return(ENOMEM);
			}
			fdadapter->adapter_busy = FALSE;
			fdadapter->error_value = 0;
			fdadapter->data_reg_excess = 0;
			/*
			 * Initialize various global data structures.
			 */
			fdadapter->sleep_anchor = EVENT_NULL;
			fdadapter->adapter_sleep_anchor = EVENT_NULL;
			for (i = 0; i < FDMAXDRIVES; i++)
				fdadapter->drive_list[i] = NULL;
		}

		/*
	 	 * If the floppy structure has not already been allocated, then
	 	 * allocate it and initialize needed parameters.
	 	 */

		/*
		 * Check to see if the drive is already initialized.  If it 
		 * is, just return with an EBUSY return code.  If not, 
		 * initialize it.
		 */  
		if (fdadapter->drive_list[drive_number] != NULL) {
			unlockl(&fd_lock);
			return(EBUSY);
		} else
		 {
			fdadapter->drive_list[drive_number] = (struct floppy *)
	    	    	    malloc(sizeof(struct floppy ));
			if (fdadapter->drive_list[drive_number] == NULL) {
				if (fdglobal_init == FALSE) {
					rc = free((caddr_t)fdadapter);
				}
				unlockl(&fd_lock);
				return(ENOMEM);
			}
			fdp = fdadapter->drive_list[drive_number];
			fdp->device_number = devno;
			fdp->drive_state = FDCLOSED;
			/* Code was removed dealing with initializing
			 * floppy data structures.
			 */
		}
		if (fdglobal_init == FALSE) {
			fddevsw.d_open = fd_open;
			fddevsw.d_close = fd_close;
			fddevsw.d_read = fd_read;
			fddevsw.d_write = fd_write;
			fddevsw.d_ioctl = fd_ioctl;
			fddevsw.d_strategy = fd_strategy;
			fddevsw.d_ttys = 0;
			fddevsw.d_select = nodev;
			fddevsw.d_config = fd_config;
			fddevsw.d_print = nodev;
			fddevsw.d_dump = nodev;
			fddevsw.d_mpx = nodev;
			fddevsw.d_revoke = nodev;
			fddevsw.d_dsdptr = NULL;
			fddevsw.d_selptr = NULL;
			fddevsw.d_opts = 0;
			rc = devswadd(devno, &fddevsw);
			if (rc != 0) {
				(void)free((caddr_t)fdp);
				(void)free((caddr_t)fdadapter);
				unlockl(&fd_lock);
				return(rc);
			}
		}
		rc = uiomove((caddr_t)(&configstruct), sizeof(configstruct),
		    UIO_WRITE, uiop);
		if (rc != 0) {
			(void)free((caddr_t)fdp);
			if (fdglobal_init == FALSE) {
				(void)devswdel(devno);
				(void)free((caddr_t)fdadapter);
			}
			unlockl(&fd_lock);
			return(rc);
		}
		configptr = &configstruct;

		/* 
		 * copy device information from the
		 * 'fd_config' structure into the
		 * 'floppy' structure.
		 */

		if (fdglobal_init == FALSE) {
			if (configptr->int_class != fdadapter->int_class) {
				rc = free((caddr_t)fdp);
				rc = free((caddr_t)fdadapter);
				rc = devswdel(devno);
				unlockl(&fd_lock);
				return(EINVAL);
			}
		}

		/*
		 * Code was removed dealing with hardware specific
		 * initialization.
		 */

		/*
		 * initialize the adapter hardware if not already done.
		 */
		if (fdglobal_init == FALSE) {
			fdglobal_init = TRUE;
		}
		break; /* end CFG_INIT case */
	default:
		unlockl(&fd_lock);
		return(EINVAL);
		break;
	} /* end switch on cmd */
	unlockl(&fd_lock);
	return(FDSUCCESS);
} /* end fd_config routine */


/*
 * NAME: fd_open
 *
 * FUNCTION: The open entry point.
 *  This routine prepares a diskette device for use.  This includes
 *  making sure the interrupt and dma services are properly set up
 *  and that the proper device characteristics are loaded for the
 *  drive type and diskette type being used.
 *
 * EXECUTION ENVIRONMENT:
 *  This routine is part of the top half of the device driver.  It 
 *  can be called only by a process and can page fault. 
 *
 * DATA STRUCTURES: 
 *  fdadapter struct - Per adapter information.
 *  floppy    struct - Per device information.
 *  intr      struct - Interrupt handler structure.
 *
 * INPUTS:
 *  devno  - the major & minor device numbers.
 *  devflag - the operation to perform and flags ORed together. 
 *
 * RETURN VALUE DESCRIPTION: FDSUCCESS (0) or error code. 
 *
 * ERROR CODES: The following errno values may be returned: 
 *  ENOTREADY  - no diskette in drive or drive door open.
 *  EBUSY      - drive already opened by another process.
 *  EINVAL     - invalid minor number. (drive or diskette type).
 *  ENXIO      - drive has been deleted.
 *  EIO        - unable to initialize dma or interrupt services.
 *
 * EXTERNAL PROCEDURES CALLED:
 *  i_init, pincode, w_init, talloc, d_init, delay, lockl, unlockl
 *
 * NOTES:
 */

int	fd_open (dev_t devno, ulong devflag)
{
	register struct floppy *fdp;
	int	i, drive_number, rc;

	lockl(&fd_lock, LOCK_SHORT);

	/*
	 * Use minor device number to determine which drive to use.
	 */

	drive_number = minor(devno) >> 6;
	if (drive_number < 0 || drive_number >= FDMAXDRIVES) {
		unlockl(&fd_lock);
		return(EINVAL);
	}

	/*
	 * Set up a pointer to the proper diskette data structure.
	 */

	fdp = fdadapter->drive_list[drive_number];

	/*
	 * See if this drive has been deleted.  If so, return.
	 */

	if (fdp == NULL) {
		unlockl(&fd_lock);
		return(ENXIO);
	}

	/*
	 * If drive is already open or is being opened, set rc to EBUSY
	 * and return.
	 */

	if (fdp->drive_state != FDCLOSED) {
		unlockl(&fd_lock);
		return(EBUSY);
	}
	fdp->drive_state = FDOPENING;

	/* 
	 * If the device driver has not been initialized, then do so.
	 */

	if (fdadapter->initialized == FALSE) {
		rc = pincode(fd_intr);
		if (rc != 0) {
			fdp->drive_state = FDCLOSED;
			unlockl(&fd_lock);
			return(rc);
		}

		/* 
		 * If the adapter structure has not been pinned, then do so.
		 */

		if (fdadapter->pinned == FALSE) {
			rc = pin((caddr_t)fdadapter,
			    sizeof(struct adapter_structure ));
			if (rc != 0) {
				fdp->drive_state = FDCLOSED;
				fdcleanup(drive_number);
				unlockl(&fd_lock);
				return(rc);
			}
			fdadapter->pinned = TRUE;
		}

		/*  
		 * Initialize the timers.
		 */

		/* 5 second timeout condition */
		fdadapter->inttimer.restart = 5;
		fdadapter->inttimer.func = fdwatchdog;
		fdadapter->inttimer.next = NULL;
		fdadapter->inttimer.prev = NULL;
		fdadapter->inttimer.count = 0;
		w_init(&fdadapter->inttimer);
		/* 10 second timeout condition */
		fdp->motor_off_time = 10;
		fdadapter->mottimer.restart = fdp->motor_off_time;
		fdadapter->mottimer.func = fdmotor_timer;
		fdadapter->mottimer.next = NULL;
		fdadapter->mottimer.prev = NULL;
		fdadapter->mottimer.count = 0;
		w_init(&fdadapter->mottimer);
		fdadapter->fdstart_timer = talloc();
		fdadapter->fdstart_timer->flags = 0;
		fdadapter->fdstart_timer->ipri = INTCLASS3;
		fdadapter->fdstart_timer->func = fdtimeout;

		/*
		 * Allocate the error log structure.
		 */

		fdadapter->fderrptr = (struct fd_err_rec *)
		    xmalloc(sizeof(struct fd_err_rec ), 3, pinned_heap);
		if (fdadapter->fderrptr == NULL) {
			fdp->drive_state = FDCLOSED;
			fdcleanup(drive_number);
			unlockl(&fd_lock);
			return(EIO);
		}

		/*
		 * Initialize the interrupt handler.
		 */

		fdadapter->fdhandler = (struct intr *)
		    xmalloc(sizeof(struct intr ), 3, pinned_heap);
		if (fdadapter->fdhandler == NULL) {
			fdp->drive_state = FDCLOSED;
			fdcleanup(drive_number);
			unlockl(&fd_lock);
			return(ENOMEM);
		}
		fdadapter->fdhandler->handler = fd_intr;
		fdadapter->fdhandler->bus_type = fdadapter->bus_type;
		fdadapter->fdhandler->flags = 0;
		fdadapter->fdhandler->level = fdadapter->bus_int_level;
		fdadapter->fdhandler->priority = INTCLASS3;
		fdadapter->fdhandler->bid = fdadapter->bus_id;
		if (i_init(fdadapter->fdhandler) != INTR_SUCC) {
			fdp->drive_state = FDCLOSED;
			fdcleanup(drive_number);
			unlockl(&fd_lock);
			return(EIO);
		}
		fdadapter->int_init = TRUE;

		/*
		 * Initialize the dma services.
		 */

		fdadapter->dma_id = d_init(fdadapter->dma_level, DMA_SLAVE,
		    fdadapter->bus_id);
		if (fdadapter->dma_id == DMA_FAIL) {
			fdp->drive_state = FDCLOSED;
			fdcleanup(drive_number);
			unlockl(&fd_lock);
			return(EIO);
		}
		fdadapter->dma_init = TRUE;
		fdadapter->initialized = TRUE;
	} /* end of adapter and structure initializations. */

	/*
	 * Pin the floppy structure for this drive.
	 */

	rc = pin((caddr_t)fdp, sizeof(struct floppy ));
	if (rc != 0) {
		fdcleanup(drive_number);
		fdp->drive_state = FDCLOSED;
		unlockl(&fd_lock);
		return(rc);
	}

	/*
	 * Load the structure 'floppy' with the default values and the minor
	 * device number.
	 */

	fdp->device_number = devno;
	fdadapter->error_value = 0;
	rc = fdload_floppy(minor(devno) & FDTYPEMASK, fdp);
	if (rc != FDSUCCESS) {
		fdcleanup(drive_number);
		fdp->drive_state = FDCLOSED;
		unlockl(&fd_lock);
		return(rc);
	}

	/*
	 * If this is the first open after config, clear the reset and
	 * enable interrupts to catch the reset interrupt before
	 * anything else is done with the hardware.
	 */

	if (fdadapter->first_open == TRUE) {
		fdadapter->error_value = 0;
		rc = fdlock_adapter(drive_number);
		if (rc != FDSUCCESS) {
			fdadapter->error_value = rc;
			unlockl(&fd_lock);
			return(fdopen_exit(fdp));
		}
		fdadapter->state = FD_INITIAL_INTERRUPT;
		w_start(&(fdadapter->inttimer));
		fdadapter->error_value = 0;
		rc = fdexecute_int_cmd(fdreset);
		if (rc != FDSUCCESS) {
			fdadapter->error_value = rc;
			unlockl(&fd_lock);
			return(fdopen_exit(fdp));
		}
		fdunlock_adapter(drive_number);
		fdadapter->state = FD_NO_STATE;
		fdadapter->first_open = FALSE;
	}
	fdp->first_move = TRUE;
		
	/*
	 * Call unlockl to release open lock.
	 */

	unlockl(&fd_lock);

	/*
	 * If the DNDELAY flag is set, the open is done.  Set drive state to
	 * FDOPEN and return.
	 */

	if (devflag & DNDELAY) {
		fdp->drive_state = FDOPEN;
		fdadapter->reset_needed = TRUE;
		return(FDSUCCESS);
	}

	/*
	 * Lock the adapter while accessing it
	 */

	fdadapter->error_value = 0;
	rc = fdlock_adapter(drive_number);
	if (rc != FDSUCCESS) {
		fdadapter->error_value = rc;
		return(fdopen_exit(fdp));
	}


	/*
	 * Check for open drive door.  If so, set rc to ENOTREADY, set
	 * state to FDCLOSED,  and return.  If the door is
	 * closed, just continue.
	 */


	/*
	 * Code for doing several error checks was removed from here.
	 */ 

	/*
	 * Check for write protect if opening for write.
	 */

	if ((devflag & DWRITE) || (devflag & DAPPEND)) {
		fdadapter->error_value = 0;
		fdsense_drive_status();
		if (fdadapter->error_value != 0) {
			return(fdopen_exit(fdp));
		}
		if (fdadapter->results.un1.names.byte0.status3 & 0x40) {
			fdadapter->error_value = EWRPROTECT;
			return(fdopen_exit(fdp));
		} /* end of SENSE_DRIVE_STATUS command processing */
	} /* end of write protect check */

	/*
	 * Call the fdtype() routine. The fdtype() routine will 
	 * either succeed and return FDSUCCESS or fail and return an errno.
	 * If it succeeds, it will load the 'floppy' structure with the correct
	 * diskette characteristics.  
	 */


	/*
	 * Unlock the adapter.
	 */

	fdunlock_adapter(drive_number);

	fdp->drive_state = FDOPEN;
	return(FDSUCCESS);
} /* end of fd_open */



/*
 * NAME: fd_close
 *
 * FUNCTION: The close entry point.
 *  This routine removes a diskette device from use.  This includes
 *  making sure the interrupt and dma services are properly cleared
 *  and that all outstanding i/o requests have been processed.
 *
 * EXECUTION ENVIRONMENT:
 *  This routine is part of the top half of the device driver.  It
 *  can be called only by a process and can page fault.
 *
 * DATA STRUCTURES:
 *  fdadapter struct - Common device driver information.
 *  floppy    struct - Per device information.
 *  intr      struct - Interrupt handler structure.
 *
 * INPUTS:
 *  devno - the major & minor device numbers.
 *
 * RETURN VALUE DESCRIPTION: none.
 *
 * ERROR CODES: The following errno values may be returned:
 *  EINVAL - invalid minor number. (drive or diskette type).
 *
 * EXTERNAL PROCEDURES CALLED:
 *  e_sleep, d_clear, lockl, unlockl
 */

int	fd_close (dev_t devno)
{
	register struct floppy *fdp;
	int	drive_number, rc;

	lockl(&fd_lock, LOCK_SHORT);
	rc = FDSUCCESS;
	drive_number = minor(devno) >> 6;
	if (drive_number < 0 || drive_number >= FDMAXDRIVES) {
		unlockl(&fd_lock);
		return(ENXIO);
	}
	fdp = fdadapter->drive_list[drive_number];
	fdp->drive_state = FDCLOSING;
	unlockl(&fd_lock);

	/* 
	 * Call fdqueue_check() to check if queue is empty.  If it is,
	 * it will turn off the drive motor and return.  Otherwise, it
	 * will sleep until processing is complete.
	 */

	rc = fdqueue_check(fdp, drive_number);
	if (rc != FDSUCCESS) {
		return(rc);
	}

	/* 
	 * Mark this drive as FDCLOSED and return.
	 */

	fdp->drive_state = FDCLOSED;
	return(FDSUCCESS);
} /* end of fd_close() */



/*
 * NAME: fd_read
 *
 * FUNCTION: The raw i/o read entry point.
 *  This routine handles raw i/o read requests.  The uphysio() kernel
 *  service handles all of the processing necessary to transform the
 *  raw i/o request to a block i/o request including calling the
 *  strategy routine.
 *
 * EXECUTION ENVIRONMENT:
 *  This routine is part of the top half of the device driver.  It
 *  can be called only by a process and can page fault.
 *
 * DATA STRUCTURES:
 *  floppy struct - Per device information.
 *
 * INPUTS:
 *  devno - the major & minor device numbers.
 *  uiop  - a pointer to a uio structure specifying caller's data.
 *
 * RETURN VALUE DESCRIPTION: returns value returned by uphysio().
 *
 * ERROR CODES: The error codes are all generated by uphysio().
 *
 * EXTERNAL PROCEDURES CALLED: uphysio
 */

int	fd_read (dev_t  devno, register struct uio *uiop)
{
	extern	int	fdnull();
	int	rc;

	rc = uphysio(uiop, B_READ, 2, devno, fd_strategy, fdnull, NULL);
	return(rc);
}



/*
 * NAME:fd_write
 *
 * FUNCTION: The raw i/o write entry point.
 *  This routine handles raw i/o write requests.  The uphysio() kernel
 *  service handles all of the processing necessary to transform the
 *  raw i/o request to a block i/o request including calling the
 *  strategy routine.
 *
 * EXECUTION ENVIRONMENT:
 *  This routine is part of the top half of the device driver.  It
 *  can be called only by a process and can page fault.
 *
 * DATA STRUCTURES:
 *  floppy struct - Per device information.
 *
 * INPUTS:
 *   devno - the major & minor device numbers.
 *   uiop  - a pointer to a uio structure specifying caller's data.
 *
 * RETURN VALUE DESCRIPTION: returns value returned by uphysio().
 *
 * ERROR CODES: The error codes are all generated by uphysio().
 *
 * EXTERNAL PROCEDURES CALLED: uphysio
 */

int	fd_write (dev_t  devno, register struct uio *uiop)
{
	extern	int	fdnull();
	int	rc;

	rc = uphysio(uiop, B_WRITE, 2, devno, fd_strategy, fdnull, NULL);
	return(rc);
}




/*
 * NAME: fd_ioctl
 *
 * FUNCTION: The ioctl entry point.
 *  This routine handles most all of the i/o besides data reads and
 *  writes and provides the diagnostics interface to the diskette
 *  drives.
 *
 * EXECUTION ENVIRONMENT:
 *  This routine is part of the top half of the device driver.  It
 *  can be called only by a process and can page fault.
 *
 * DATA STRUCTURES:
 *  fdadapter struct - Common device driver information.
 *  floppy    struct - Per device information.
 *  devinfo   struct - A standard system structure used with the
 *                     IOCINFO ioctl.
 *  fdinfo    struct - Used to pass information for the
 *                     FDIOCGINFO and the FDIOCSINFO ioctls.
 *  fd_status struct - Used to pass the status of the drive and
 *                     the device driver with the FDIOCSTATUS
 *                     ioctl.
 *  fdparms   struct - Used to pass information for the
 *                     FDIOCGETPARMS and FDIOCSETPARMS ioctls.
 *
 * INPUTS:
 *  devno - a pointer to a buffer header structure.
 *  op    - the ioctl operation to perform.
 *  arg   - a parameter used to pass data to and from user space.
 *          The actual use is dependent on the operation.
 *
 * RETURN VALUE DESCRIPTION: none.
 *
 * ERROR CODES: The following errno values may be returned:
 *  EINVAL - either the 'op' parameter, the 'arg' parameter, or
 *           data pointed to by the 'arg' parameter is invalid.
 *
 * EXTERNAL PROCEDURES CALLED:
 *  lockl, unlockl, pin, unpin,copyin, copyout, xmalloc, xmfree,
 *  xmemat, xmemdt, xmemin, malloc, free
 */

int	fd_ioctl (dev_t devno, int op, register long arg, ulong devflag)
{
	register struct floppy *fdp;
	struct devinfo devinfo;
	struct fdinfo fdinfo;
	struct fd_status fdstatus;
	struct fdparms fdparms;
	int	drive_number, rc = FDSUCCESS, local_error_value;

	lockl(&fd_lock, LOCK_SHORT);
	drive_number = minor(devno) >> 6;
	fdp = fdadapter->drive_list[drive_number];

	switch (op) {

	/* 
	 * The following ioctl operation is defined for every device
	 * that uses the ioctl interface.
	 *
	 *
	 *  IOCINFO - returns some information about the diskette.
	 *      This is a standard ioctl option that can be issued
	 *      to find out information about any device that uses
	 *      ioctls.  A pointer to a structure of type devinfo
	 *      should be passed in the 'arg' parameter.  The
	 *      information about the diskette will be loaded
	 *      into the devinfo structure.
	 */

	case IOCINFO:
		devinfo.devtype = DD_DISK;
		devinfo.flags = DF_RAND;
		devinfo.un.dk.bytpsec = (short)(fdp->bytes_per_sector);
		devinfo.un.dk.secptrk = (short)(fdp->sectors_per_track);
		devinfo.un.dk.trkpcyl = (short)(fdp->tracks_per_cylinder);
		devinfo.un.dk.numblks = (long)(fdp->number_of_blocks);

		/* 
		 * Call copyout to copy the devinfo structure
		 * to the user.
		 */

		rc = copyout((char *)(&devinfo), (char *)(arg),
		    sizeof(struct devinfo ));
		if (rc != 0) {
			unlockl(&fd_lock);
			return(rc);
		}
		break; /* end of IOCINFO */
		/* Code was removed dealing with other ioctl's.  The ioctl's
		 * are generally modeled after the IOCINFO ioctl.
		 */
	default:
		rc = EINVAL;
		break; /* end of default case */
	} /* end of switch on op */
	unlockl(&fd_lock);
	return(rc);
} /* end of fd_ioctl() */


/*
 * The following are the top half internal device driver routines.
 * All routines assume that the adapter lock has been secured by the 
 * calling routine.
 */


	/* Various utility functions have been removed. */

int	fdopen_exit (struct floppy *fdp)
{
	int	local_error_value, rc;
	uchar	drive_number;

	drive_number = minor(fdp->device_number) >> 6;
	local_error_value = fdadapter->error_value;
	fddisable_controller();
	rc = unpin((caddr_t)fdp, sizeof(struct floppy));
	fdcleanup(drive_number);
	fdp->drive_state = FDCLOSED;
	fdunlock_adapter(drive_number);
	return(local_error_value);
}
	/* Various utility routines have been removed. */