/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* bos720 src/bos/usr/samples/fd/fd_bot.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[] = "@(#)66 1.1 src/bos/usr/samples/fd/fd_bot.c, fdsamp, bos720 10/12/93 14:35:59"; /* * COMPONENT_NAME: FDSAMP (Diskette Device Driver) * * * FUNCTIONS: fd_config, fd_open, fd_close, fd_read, fd_write, * fd_strategy, fd_intr, fd_ioctl * * INTERNAL FUNCTIONS: fdcheck_sequence, fdcleanup, fddisk_changed, * fddoor_check, fderror, fdexecute_command, * fdexecute_sequence, fdget_results, fdgetbyte, fdio, * fdissue_command, fdload_floppy, fdlog_error, fdnull, * fdputbyte, fdread, * fdsettle_test, fdsettle_timer_handler, fdspeed_test, * fdstart_sequence, fdstartio, fdtimeout, fdtimer, * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fd_local.h" struct adapter_structure *fdadapter; /* allocate the adapter structure pointer */ /* * keep config parameters around to reset to config values. */ struct fdconfig_parameters fdconfparms[FDMAXDRIVES]; /* * NAME: fd_strategy * * FUNCTION: The block i/o entry point. * This routine handles all block i/o requests. It checks the * parameters of the i/o request and maintains a buffer queue for * each drive. If the queue for a drive is empty it calls fdio to * try and start the i/o immediately. * Otherwise it just sticks the i/o request at the * end of the drive's queue. * * EXECUTION ENVIRONMENT: * This routine is part of the bottom half of the device driver. * It is pinned in memory and can be called from the process and * interrupt levels. * * DATA STRUCTURES: * fdadapter struct - Common device driver information. * floppy struct - Per device information. * * INPUTS: * bp - a pointer to a buffer header structure. * * RETURN VALUE DESCRIPTION: none. * * ERROR CODES: Value set in the buf struct by fdio. * * EXTERNAL PROCEDURES CALLED: i_enable, i_disable, iodone */ int fd_strategy (register struct buf *bp) { register struct floppy *fdp; register struct buf *nextbp; int old_level, drive_number; /* * Loop until there are no more buf structs to process. */ while (bp != NULL) { drive_number = minor(bp->b_dev) >> 6; nextbp = bp->av_forw; if (drive_number < 0 || drive_number >= FDMAXDRIVES) { bp->b_error = EINVAL; bp->b_flags |= B_ERROR; bp->b_resid = bp->b_bcount; iodone(bp); } else { old_level = i_disable(fdadapter->int_class); fdp = fdadapter->drive_list[drive_number]; /* * See if there are any buffer headers in the queue. * If there are, put the buffer header at the end of * the queue. If the queue is empty, put the buffer * header in the queue and call fdio(). */ /* * if queue is not empty, just put in queue */ if (fdp->headptr != NULL) { /* * make old tail point to the new tail */ fdp->tailptr->av_forw = bp; fdp->tailptr = bp; /* set up new tail */ i_enable(old_level); } else { /* * else start the i/o */ fdp->headptr = bp; /* start header queue */ fdp->tailptr = fdp->headptr; i_enable(old_level); fdio(fdp->headptr); } } /* end of valid drive number processing */ bp = nextbp; } /* end of while loop */ return(FDSUCCESS); } /* end of fd_strategy() routine */ /* * NAME: fd_intr * * FUNCTION: The interrupt handler. * This routine handles all device interrupts. The operating * system's first level interrupt handler calls this routine and * fd_intr() first must decide if the interrupt was from the diskette * controller. If the interrupt was from the diskette, this * routine clears the interrupt, processes the interrupt, and * decides what should be done next. * * EXECUTION ENVIRONMENT: * This routine is part of the bottom half of the device driver. * It is pinned in memory and can be called only by the operating * system's first level interrupt handler. * * DATA STRUCTURES: * fdadapter struct - Common device driver information. * floppy struct - Per device information. * result_bytes struct - Controller result phase data. * buf struct - A system structure that describes i/o * operations. This is what is stored in * the queue created by the strategy * routine. * dummy struct - A floppy type structure allocated in the * stack to hold information after an * unsolicited reset has caused an * interrupt. This information is just * thrown away, but needed a place to be * sent to. * * INPUTS: none. * * RETURN VALUE DESCRIPTION: * INTR_SUCC - this was a diskette interrupt and it has been * processed. * INTR_FAIL - this was not a diskette interrupt. * * ERROR CODES: none. * * EXTERNAL PROCEDURES CALLED: * i_reset, tstart, tstop, d_slave, d_complete, iodone, curtime, * e_wakeup, w_stop * * NOTES: * Errors are handled by either setting a bit that will be * read when an FDIOCSTATUS ioctl is made or by other * routines that fd_intr() calls. This routine does not set * any errno values directly. */ int fd_intr () { register struct floppy *fdp; int rc, temp; uchar change_line_value, interrupt_check; struct buf *bp; ulong elapsed_time; struct pio_parms piop; /* Machine specific code to determine the ownership of the * interrupt. */ w_stop(&(fdadapter->inttimer)); fdp = fdadapter->drive_list[fdadapter->active_drive]; switch(fdadapter->state) { case FD_INITIAL_INTERRUPT: fdsense_interrupt(); i_reset(fdadapter->fdhandler); fdadapter->state = FD_NO_STATE; if (fdadapter->first_open == FALSE) { fdlog_error(fdp, 0xff); /* log spurious interrupt */ } else { e_wakeup(&(fdadapter->sleep_anchor)); } break; case FD_TYPE1_WAKEUP: fdadapter->command.total_bytes = 0; fdadapter->results.total_bytes = 7; piop.bus_val = BUSIO_ATT(fdadapter->bus_id, fdadapter->io_address); fdadapter->error_value = pio_assist((caddr_t)(&piop), fddata_reg_pio, fddata_reg_pio_recovery); BUSIO_DET(piop.bus_val); i_reset(fdadapter->fdhandler); e_wakeup(&(fdadapter->sleep_anchor)); break; case FD_TYPE2_WAKEUP: fdsense_interrupt(); i_reset(fdadapter->fdhandler); e_wakeup(&(fdadapter->sleep_anchor)); break; case FD_FORMAT: /* * Code removed dealing with handling of interrupts * during the formating of disks. */ break; case FD_RW: switch(fdadapter->sub_state) { case FD_RW_SEEK1: fdadapter->error_value = 0; fdsense_interrupt(); if (fdadapter->error_value != 0) { return(fdrw_exit(fdp, fdadapter->error_value)); } i_reset(fdadapter->fdhandler); if ((fdadapter->results.un1.names.byte0.status0 & FDNORMAL) != 0) { if (fdp->retry_flag == FALSE) { return(fdrw_exit(fdp, fdadapter->error_value)); } fdadapter->sub_state = FD_RW_RECAL1; fdadapter->error_value = 0; fdrecalibrate(); if (fdadapter->error_value != 0) { return(fdrw_exit(fdp, fdadapter->error_value)); } } else { fdadapter->error_value = 0; change_line_value = fdread_change_line(); if (fdadapter->error_value != 0) { return(fdrw_exit(fdp, fdadapter->error_value)); } if (change_line_value & FDDISK_CHANGE) { if (fdp->first_move == TRUE) { fdadapter->sub_state = FD_RW_DRCHK_RECAL; fdadapter->error_value = 0; fdrecalibrate(); if (fdadapter->error_value != 0) { return(fdrw_exit(fdp, fdadapter-> error_value)); } fdp->first_move = FALSE; } else { return(fdrw_exit(fdp, fdadapter-> error_value)); } } else { if (fdp->headptr->b_flags & B_READ) fdp->dma_flags = DMA_READ; else fdp->dma_flags = 0; d_slave(fdadapter->dma_id, fdp->dma_flags, (char *)(fdp->headptr->b_un.b_addr + fdp->buf_offset), fdp->current.transfer_length, &(fdp->headptr->b_xmemd)); fdadapter->sub_state = FD_RW_SUB; fdadapter->error_value = 0; fdrw(); if (fdadapter->error_value != 0) { return(fdrw_exit(fdp, fdadapter->error_value)); } } } break; case FD_RW_SUB: rc = d_complete(fdadapter->dma_id, fdp->dma_flags, (char *)(fdp->headptr->b_un.b_addr + fdp->buf_offset), fdp->current.transfer_length, &(fdp->headptr->b_xmemd), NULL); fdadapter->command.total_bytes = 0; fdadapter->results.total_bytes = 7; piop.bus_val= BUSIO_ATT(fdadapter->bus_id, fdadapter->io_address); fdadapter->error_value = pio_assist((caddr_t)(&piop), fddata_reg_pio, fddata_reg_pio_recovery); BUSIO_DET(piop.bus_val); i_reset(fdadapter->fdhandler); if (fdadapter->error_value == 0) if (rc != DMA_SUCC) fdadapter->error_value = EIO; if (fdadapter->error_value != 0) { return(fdrw_exit(fdp, fdadapter->error_value)); } if ((fdadapter->results.un1.names.byte0.status0 & FDNORMAL) != 0) { if (fdp->retry_flag == FALSE) { fdassign_error(fdp); return(fdrw_exit(fdp, fdadapter->error_value)); } if (fdp->simple_count1 >= FDMAX_SIMPLE_RETRIES1) { /* * Code was removed dealing * with retry recovery. */ fdp->complex_count++; fdadapter->sub_state = FD_RW_RESET; fdadapter->error_value = 0; fdreset(); if (fdadapter->error_value != 0) { return(fdrw_exit(fdp, fdadapter->error_value)); } } else { fdp->simple_count1++; if (fdp->headptr->b_flags & B_READ) fdp->dma_flags = DMA_READ; else fdp->dma_flags = 0; d_slave (fdadapter->dma_id, fdp->dma_flags, (char *)(fdp->headptr->b_un.b_addr + fdp->buf_offset), fdp->current.transfer_length, &(fdp->headptr->b_xmemd)); fdadapter->sub_state = FD_RW_SUB; fdadapter->error_value = 0; fdrw(); if (fdadapter->error_value != 0) { return(fdrw_exit(fdp, fdadapter->error_value)); } } } else { fdp->total_bcount += fdp->current.transfer_length; /* * Code was removed dealing with * updating the data transfer counters. */ } break; } /* Removed code dealing with other conditions. */ default: break; } return(INTR_SUCC); } /* end of fd_intr() */ /* * The following are the bottom half internal device driver routines. */ /* * NAME: fdcleanup * * FUNCTION: Cleans up initialization stuff prior to exiting. * This routine is called just before exiting in fd_open and * fd_close. * * EXECUTION ENVIRONMENT: * This routine is part of the top half of the device driver. It * can be called only from the process level. * * DATA STRUCTURES: none. * * INPUTS: none. * * RETURN VALUE DESCRIPTION: none. * * ERROR CODES: none. * * EXTERNAL PROCEDURES CALLED: * i_clear, d_clear, w_clear, tstop, tfree, unpincode */ void fdcleanup (int drive_number) { int i, cleanup_flag, rc; cleanup_flag = TRUE; for (i = 0; i < FDMAXDRIVES; i++) { if (i != drive_number) if (fdadapter->drive_list[i] != NULL) if (fdadapter->drive_list[i]->drive_state != FDCLOSED) cleanup_flag = FALSE; } if (cleanup_flag == TRUE) { if (fdadapter->fderrptr != NULL) { rc = xmfree((caddr_t)fdadapter->fderrptr, pinned_heap); } if (fdadapter->int_init) { i_clear(fdadapter->fdhandler); fdadapter->int_init = FALSE; } if (fdadapter->dma_init) { d_clear(fdadapter->dma_level); fdadapter->dma_init = FALSE; } if (fdadapter->inttimer.func != NULL) { w_stop(&fdadapter->inttimer); w_clear(&fdadapter->inttimer); fdadapter->inttimer.func = NULL; } if (fdadapter->mottimer.func != NULL) { w_stop(&fdadapter->mottimer); w_clear(&fdadapter->mottimer); fdadapter->mottimer.func = NULL; } if (fdadapter->fdstart_timer != NULL) { tstop(fdadapter->fdstart_timer); tfree(fdadapter->fdstart_timer); fdadapter->fdstart_timer = NULL; } if (fdadapter->pinned == TRUE) { rc = unpin((caddr_t)fdadapter, sizeof(struct adapter_structure)); fdadapter->pinned = FALSE; } rc = unpincode(fd_intr); fdadapter->initialized = FALSE; } } /* end of fdcleanup() */ /* * NAME: fdread_change_line * * FUNCTION: Reads the diskette changed register * * EXECUTION ENVIRONMENT: * This routine is part of the bottom half of the device driver. It * can be called from the process or the interrupt level. * * DATA STRUCTURES: none. * * INPUTS: * * RETURN VALUE DESCRIPTION: * * ERROR CODES: none. * * EXTERNAL PROCEDURES CALLED: none. */ uchar fdread_change_line () { uchar rc; struct pio_parms piop; piop.bus_val= BUSIO_ATT(fdadapter->bus_id, fdadapter->io_address); piop.reg = FDDISK_CHANGED_REG; piop.read = TRUE; fdadapter->error_value = pio_assist((caddr_t)(&piop), fdcntl_reg_pio, fdcntl_reg_pio_recovery); rc = piop.data; BUSIO_DET(piop.bus_val); return(rc); } /* end of fdread_change_line() */ /* * NAME: fdio * * FUNCTION: Sets up the floppy structure for i/o and calls fdstartio. * This routine translates the starting block of a read or write * into the cylinder, head, sector address required by the * controller. It then computes the total number of reads or * writes needed and initializes the fields in the floppy structure * that are used to execute a read or write. * * EXECUTION ENVIRONMENT: * This routine is part of the bottom half of the device driver. It * can be called at the process or interrupt level. * * DATA STRUCTURES: * fdadapter struct - Common device driver information. * floppy struct - Per device information. * * INPUTS: * bp - a pointer to the buf struct describing the transfer. * * RETURN VALUE DESCRIPTION: none. * * ERROR CODES: none. * * EXTERNAL PROCEDURES CALLED: none. * * NOTES: * This routine is called once for each buf struct * processed. It is called by both fd_strategy() and * fd_intr() to initiate a new i/o sequence for a given * buf struct. */ void fdio (register struct buf *bp) { register struct floppy *fdp; daddr_t last_block; uchar drive_number; ushort temp; ulong bytes_left; int old_level, evalue; old_level = i_disable(fdadapter->int_class); drive_number = minor(bp->b_dev) >> 6; if (fdadapter->adapter_busy == FALSE) { fdadapter->adapter_busy = TRUE; fdadapter->active_drive = drive_number; fdp = fdadapter->drive_list[drive_number]; i_enable(old_level); fdp->simple_count1 = 0; fdp->complex_count = 0; fdp->simple_count2 = 0; fdp->total_bcount = 0; bp->b_resid = 0; /* * If the byte transfer length is not a multiple of 512 bytes, * set b_error to EINVAL and return. */ if (bp->b_bcount % fdp->bytes_per_sector) { bp->b_flags |= B_ERROR; bp->b_error = EINVAL; bp->b_resid += bp->b_bcount; fdp->headptr = fdp->headptr->av_forw; iodone(bp); fdunlock_adapter(drive_number); return; } /* * Verify that i/o does not start past the end of the diskette. * Also, verify that the starting block number is non-negative. * If it is not, return ENXIO. */ if ((bp->b_blkno < 0) || (bp->b_blkno >= fdp->number_of_blocks)) { if (bp->b_blkno == fdp->number_of_blocks) { if (bp->b_flags & B_READ) { bp->b_error = 0; } else { bp->b_flags |= B_ERROR; bp->b_error = ENXIO; } bp->b_resid = bp->b_bcount; } else { bp->b_flags |= B_ERROR; bp->b_resid = bp->b_bcount; bp->b_error = ENXIO; } fdp->headptr = fdp->headptr->av_forw; evalue = bp->b_error; iodone(bp); fdunlock_adapter(drive_number); return; } /* * See if a request goes past the end of the * diskette. */ fdp->modified_bcount = bp->b_bcount; last_block = bp->b_blkno + (fdp->modified_bcount / fdp->bytes_per_sector); if (last_block > fdp->number_of_blocks) { /* * Request starts before the end of the * diskette but runs over the end. * Set up to do a partial data transfer * and return the number of bytes * not transferred in the b_resid * field. */ bp->b_resid = (last_block - fdp->number_of_blocks) * fdp->bytes_per_sector; fdp->modified_bcount -= bp->b_resid; } /* * Translate block number into physical address. */ /* * Set up the other fields in 'floppy' that are used for * keeping track of reads and writes. */ if (fdadapter->motor_on != drive_number) { /* * If the motor is not on, set a timer to pop when the * drive has spun up. */ if (fdadapter->reset_needed == TRUE) { fdadapter->state = FD_IO_RESET; fdadapter->error_value = 0; fdreset(); evalue = fdadapter->error_value; if (evalue != 0) { fdadapter->reset_needed = TRUE; bp->b_flags |= B_ERROR; bp->b_error = evalue; bp->b_resid = bp->b_bcount; fdp->headptr = fdp->headptr->av_forw; iodone(bp); fdunlock_adapter(drive_number); } return; } fdadapter->error_value = 0; fdselect_drive(); evalue = fdadapter->error_value; if (evalue != 0) { fdadapter->reset_needed = TRUE; bp->b_flags |= B_ERROR; bp->b_error = evalue; bp->b_resid = bp->b_bcount; fdp->headptr = fdp->headptr->av_forw; iodone(bp); fdunlock_adapter(drive_number); return; } fdadapter->error_value = 0; fdspecify(); evalue = fdadapter->error_value; if (evalue != 0) { fdadapter->reset_needed = TRUE; bp->b_flags |= B_ERROR; bp->b_error = evalue; bp->b_resid = bp->b_bcount; fdp->headptr = fdp->headptr->av_forw; iodone(bp); fdunlock_adapter(drive_number); return; } fdadapter->error_value = 0; fdset_data_rate(); evalue = fdadapter->error_value; if (evalue != 0) { fdadapter->reset_needed = TRUE; bp->b_flags |= B_ERROR; bp->b_error = evalue; bp->b_resid = bp->b_bcount; fdp->headptr = fdp->headptr->av_forw; iodone(bp); fdunlock_adapter(drive_number); return; } fdadapter->fdstart_timer->timeout.it_value.tv_sec = 0; fdadapter->fdstart_timer->timeout.it_value.tv_nsec = fdp->motor_start * 1000000; fdadapter->fdstart_timer->func_data = (ulong)fdp; fdadapter->state = FD_RW; fdadapter->sub_state = FD_RW_DELAY_TIMER; tstart(fdadapter->fdstart_timer); } else { old_level = i_disable(fdadapter->int_class); fdadapter->state = FD_RW; fdadapter->error_value = 0; if (fdadapter->reset_needed == TRUE) { fdadapter->sub_state = FD_RW_RESET; fdreset(); } else { fdadapter->sub_state = FD_RW_SEEK1; fdseek(); } evalue = fdadapter->error_value; i_enable(old_level); if (evalue != 0) { fdadapter->reset_needed = TRUE; bp->b_flags |= B_ERROR; bp->b_error = evalue; bp->b_resid = bp->b_bcount; fdp->headptr = fdp->headptr->av_forw; iodone(bp); fdunlock_adapter(drive_number); return; } } } else { i_enable(old_level); } } /* end of fdio() */ /* * NAME: fdload_floppy * * FUNCTION: Loads the proper diskette characteristics. * This routine loads the floppy structure with the correct * diskette characteristics for the type indicated. * * EXECUTION ENVIRONMENT: * This routine is part of the bottom half of the device driver. It * can be called from the process or the interrupt level. * * DATA STRUCTURES: none. * * INPUTS: * value - the code of the diskette type to use. * fdp - a pointer to the floppy structure to use. * * RETURN VALUE DESCRIPTION: * FDSUCCESS - the characteristics were successfully loaded. * * ERROR CODES: The following errno values may be returned: * EINVAL - the 'value' parameter passed was invalid. * - errno returned by fdissue_command. * * EXTERNAL PROCEDURES CALLED: none. */ int fdload_floppy (int value, register struct floppy *fdp) { int rc = FDSUCCESS; uchar drive_number; drive_number = (uchar)(minor(fdp->device_number) >> 6); fdadapter->mottimer.restart = fdp->motor_off_time; switch (value) { case FDGENERIC: /* Code has been removed to set the data structures to * the default values. */ break; /* end of FDGENERIC case */ case FD1440_3: /* Code has been removed to set the data structures to the * proper values for a 3.5" 1.44Mb floppy. */ break; /* end of FD1440_3 case */ case FD720_3: /* Code has been removed to set the data structures to the * proper values for a 3.5" 720Kb floppy. */ break; /* end of FD720_3 case */ case FD1200_5: /* Code has been removed to set the data structures to the * proper values for a 5.25" 1.2Mb floppy. */ break; /* end of FD1200_5 case */ case FD360_5: /* Code has been removed to set the data structures to the * proper values for a 5.25" 360Kb floppy. */ break; /* end of FD360_5 case */ default: fdp->drive_state = FDCLOSED; fdp->diskette_type = FDUNKNOWN; rc = EINVAL; break; } return(rc); } /* end of fdload_floppy() */ /* Various utility functions were removed from here. */ /* * NAME: fdwatchdog * * FUNCTION: * * EXECUTION ENVIRONMENT: * This routine is part of the bottom half of the device driver. It * can be called from the process or the interrupt level. * * DATA STRUCTURES: none. * * INPUTS: none. * * RETURN VALUE DESCRIPTION: none. * * ERROR CODES: none. * * EXTERNAL PROCEDURES CALLED: none. */ void fdwatchdog (struct watchdog *timer) { register struct floppy *fdp; int rc, old_level; old_level = i_disable(fdadapter->int_class); fdp = fdadapter->drive_list[fdadapter->active_drive]; switch (fdadapter->state) { case FD_RW: rc = d_complete(fdadapter->dma_id, fdp->dma_flags, (char *)(fdp->headptr->b_un.b_addr + fdp->buf_offset), fdp->current.transfer_length, &(fdp->headptr->b_xmemd), NULL); fdrw_exit(fdp, ENOTREADY); break; case FD_SPEED: break; case FD_SETTLE: switch (fdadapter->sub_state) { case FD_SETTLE_READ1: case FD_SETTLE_READ2: rc = d_complete(fdadapter->dma_id, DMA_READ, (char *)fdadapter->speed_buffer, fdp->bytes_per_sector, &fdadapter->xdp, NULL); break; case FD_SETTLE_WRITE: rc = d_complete(fdadapter->dma_id, 0, (char *)fdadapter->speed_buffer, fdp->bytes_per_sector, &fdadapter->xdp, NULL); break; } break; case FD_FORMAT: break; case FD_IO_RESET: break; case FD_TYPE1_WAKEUP: case FD_TYPE2_WAKEUP: case FD_INITIAL_INTERRUPT: break; default: break; } fdlog_error(fdp, 0xef); fdadapter->reset_needed = TRUE; fdadapter->error_value = ENOTREADY; e_wakeup(&(fdadapter->sleep_anchor)); fdadapter->state = FD_NO_STATE; fdadapter->sub_state = NULL; i_enable(old_level); } /* end of fdwatchdog() */ int fdcntl_reg_pio (caddr_t piop) { if (((struct pio_parms *)(piop))->read == TRUE) { ((struct pio_parms *)(piop))->data = FDREAD((ulong)((struct pio_parms *)(piop))->bus_val + ((struct pio_parms *)(piop))->reg); } else { FDWRITE((ulong)((struct pio_parms *) (piop))->bus_val + ((struct pio_parms *)(piop))->reg, ((struct pio_parms *)(piop))->data); } return(FDSUCCESS); } int fdcntl_reg_pio_recovery (caddr_t piop, int action) { if (action == PIO_NO_RETRY) { fdadapter->reset_needed = TRUE; fdlog_error(NULL, 2); /* log permanent PIO error */ } else { fdlog_error(NULL, 1); /* log temporary PIO error */ if (((struct pio_parms *)(piop))->read == TRUE) { FDREAD((ulong)((struct pio_parms *)(piop))->bus_val + ((struct pio_parms *)(piop))->reg); } else { FDWRITE((ulong)((struct pio_parms *)(piop))->bus_val + ((struct pio_parms *)(piop))->reg, ((struct pio_parms *)(piop))->data); } } return(FDSUCCESS); } void fdset_data_rate () { /* Code for setting the data rate was removed from here. */ } void fdreset () { /* Machine dependent reset code was removed from here. */ } void fdsoft_reset () { /* Machine dependent reset code was removed from here. */ } void fdselect_drive () { /* Machine dependent code to select the active drive. */ } void fdenable_controller () { /* Machine dependent code to enable the controller was removed. */ } void fddisable_controller () { /* Machine dependent code to disable the controller was removed. */ } /* Several machine dependent utility routines have been removed. */ void fdrw () { uchar drive_number, command; register struct floppy *fdp; struct pio_parms piop; int old_level; drive_number = fdadapter->active_drive; fdp = fdadapter->drive_list[drive_number]; fdadapter->d_comp_needed = FALSE; if (fdp->headptr->b_flags & B_READ) command = FDREAD_DATA; else command = FDWRITE_DATA; fdadapter->command.total_bytes = 9; fdadapter->results.total_bytes = 0; fdadapter->command.un1.cmds.command1 = command | FDMT | FDMFM; fdadapter->command.un1.cmds.command2 = (fdp->current.head << 2) | drive_number; fdadapter->command.un1.cmds.byte2.track = fdp->current.cylinder; fdadapter->command.un1.cmds.byte3.head = fdp->current.head; fdadapter->command.un1.cmds.byte4.sector = fdp->current.sector; fdadapter->command.un1.cmds.byte5.sector_size = fdp->sector_size; fdadapter->command.un1.cmds.eot_sector = fdp->sectors_per_track; fdadapter->command.un1.cmds.gap_length = fdp->gap; fdadapter->command.un1.cmds.data_length = fdp->data_length; w_start(&(fdadapter->inttimer)); piop.bus_val = BUSIO_ATT(fdadapter->bus_id, fdadapter->io_address); old_level = i_disable(fdadapter->int_class); fdadapter->error_value = pio_assist((caddr_t)(&piop), fddata_reg_pio, fddata_reg_pio_recovery); BUSIO_DET(piop.bus_val); if (fdadapter->error_value != 0) { w_stop(&(fdadapter->inttimer)); fdadapter->d_comp_needed = TRUE; } else { if (fdadapter->motor_on != FDNO_DRIVE) { w_stop(&(fdadapter->mottimer)); w_start(&(fdadapter->mottimer)); } } i_enable(old_level); } /* end of fdrw() */ /* Several machine dependent utility routines have been removed. */ /* process level only */ int fdlock_adapter (int drive_number) { int old_level, rc; old_level = i_disable(fdadapter->int_class); while (fdadapter->adapter_busy == TRUE) { rc = e_sleep(&(fdadapter->adapter_sleep_anchor), EVENT_SIGRET); if (rc == EVENT_SIG) { i_enable(old_level); return(EINTR); } } fdadapter->adapter_busy = TRUE; fdadapter->active_drive = drive_number; i_enable(old_level); return(FDSUCCESS); } void fdunlock_adapter (int drive_number) { int old_level, i; struct floppy *fdp; old_level = i_disable(fdadapter->int_class); fdadapter->adapter_busy = FALSE; for (i = 0; i < FDMAXDRIVES; i++) { if (i != drive_number) { fdp = fdadapter->drive_list[i]; if (fdp != NULL) { if (fdp->headptr != NULL) { fdio(fdp->headptr); i_enable(old_level); return; } } } } fdp = fdadapter->drive_list[drive_number]; if (fdp->headptr != NULL) { fdio(fdp->headptr); } else { e_wakeup(&(fdadapter->adapter_sleep_anchor)); } i_enable(old_level); } void fdtimeout (register struct trb *fdstart_timer_ptr) { uchar evalue, drive_number; register struct buf *bp; register struct floppy *fdp; int old_level; old_level = i_disable(fdadapter->int_class); fdadapter->state = FD_RW; fdadapter->error_value = 0; if (fdadapter->reset_needed == TRUE) { fdadapter->sub_state = FD_RW_RESET; fdreset(); } else { fdadapter->sub_state = FD_RW_SEEK1; fdseek(); } evalue = fdadapter->error_value; if (evalue != 0) { fdadapter->reset_needed = TRUE; drive_number = fdadapter->active_drive; fdp = (register struct floppy * )fdstart_timer_ptr->func; bp = (register struct buf * )fdp->headptr; bp->b_flags |= B_ERROR; bp->b_error = evalue; bp->b_resid += fdp->modified_bcount - fdp->total_bcount; fdp->headptr = fdp->headptr->av_forw; iodone(bp); fdunlock_adapter(drive_number); } i_enable(old_level); } int fdqueue_check (register struct floppy *fdp, int drive_number) { int old_level, rc, local_error_value; old_level = i_disable(fdadapter->int_class); while (fdp->headptr != NULL) { rc = e_sleep(&(fdadapter->adapter_sleep_anchor), EVENT_SIGRET); if (rc == EVENT_SIG) { fdp->drive_state = FDOPEN; i_enable(old_level); return(EINTR); } } /* * Get control of the adapter, turn off the drive, and * call fdcleanup to finish processing. */ rc = fdlock_adapter(drive_number); if (rc != FDSUCCESS) { fdp->drive_state = FDOPEN; i_enable(old_level); return(rc); } i_enable(old_level); fdadapter->error_value = 0; fddisable_controller(); if (fdadapter->error_value != 0) { fdp->drive_state = FDOPEN; local_error_value = fdadapter->error_value; fdunlock_adapter(drive_number); return(local_error_value); } fdcleanup(drive_number); fdunlock_adapter(drive_number); return(FDSUCCESS); } void fdassign_error(register struct floppy *fdp) { switch(fdadapter->results.un1.names.byte1.status1) { case 0x02: /* Write protected */ fdadapter->error_value = EWRPROTECT; break; case 0x01: /* Missing address mark */ if (fdadapter->results.un1.names.status2 == 0x01) fdadapter->error_value = EIO; else fdadapter->error_value = EFORMAT; break; default: fdadapter->error_value = EIO; break; } } /* Several utility routines have been removed. */