1159 lines
33 KiB
C
1159 lines
33 KiB
C
/* $NetBSD: rf.c,v 1.16 2007/10/08 16:41:12 ad Exp $ */
|
|
/*
|
|
* Copyright (c) 2002 Jochen Kunz.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. The name of Jochen Kunz may not be used to endorse or promote
|
|
* products derived from this software without specific prior
|
|
* written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY JOCHEN KUNZ
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JOCHEN KUNZ
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
TODO:
|
|
- Better LBN bound checking, block padding for SD disks.
|
|
- Formatting / "Set Density"
|
|
- Better error handling / detailed error reason reportnig.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: rf.c,v 1.16 2007/10/08 16:41:12 ad Exp $");
|
|
|
|
/* autoconfig stuff */
|
|
#include <sys/param.h>
|
|
#include <sys/device.h>
|
|
#include <sys/conf.h>
|
|
#include "locators.h"
|
|
#include "ioconf.h"
|
|
|
|
/* bus_space / bus_dma */
|
|
#include <machine/bus.h>
|
|
|
|
/* UniBus / QBus specific stuff */
|
|
#include <dev/qbus/ubavar.h>
|
|
|
|
/* disk interface */
|
|
#include <sys/types.h>
|
|
#include <sys/disklabel.h>
|
|
#include <sys/disk.h>
|
|
|
|
/* general system data and functions */
|
|
#include <sys/systm.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/ioccom.h>
|
|
|
|
/* physio / buffer handling */
|
|
#include <sys/buf.h>
|
|
#include <sys/bufq.h>
|
|
|
|
/* tsleep / sleep / wakeup */
|
|
#include <sys/proc.h>
|
|
/* hz for above */
|
|
#include <sys/kernel.h>
|
|
|
|
/* bitdefinitions for RX211 */
|
|
#include <dev/qbus/rfreg.h>
|
|
|
|
|
|
#define RFS_DENS 0x0001 /* single or double density */
|
|
#define RFS_AD 0x0002 /* density auto detect */
|
|
#define RFS_NOTINIT 0x0000 /* not initialized */
|
|
#define RFS_PROBING 0x0010 /* density detect / verify started */
|
|
#define RFS_FBUF 0x0020 /* Fill Buffer */
|
|
#define RFS_EBUF 0x0030 /* Empty Buffer */
|
|
#define RFS_WSEC 0x0040 /* Write Sector */
|
|
#define RFS_RSEC 0x0050 /* Read Sector */
|
|
#define RFS_SMD 0x0060 /* Set Media Density */
|
|
#define RFS_RSTAT 0x0070 /* Read Status */
|
|
#define RFS_WDDS 0x0080 /* Write Deleted Data Sector */
|
|
#define RFS_REC 0x0090 /* Read Error Code */
|
|
#define RFS_IDLE 0x00a0 /* controller is idle */
|
|
#define RFS_CMDS 0x00f0 /* command mask */
|
|
#define RFS_OPEN_A 0x0100 /* partition a open */
|
|
#define RFS_OPEN_B 0x0200 /* partition b open */
|
|
#define RFS_OPEN_C 0x0400 /* partition c open */
|
|
#define RFS_OPEN_MASK 0x0f00 /* mask for open partitions */
|
|
#define RFS_OPEN_SHIFT 8 /* to shift 1 to get RFS_OPEN_A */
|
|
#define RFS_SETCMD(rf, state) ((rf) = ((rf) & ~RFS_CMDS) | (state))
|
|
|
|
|
|
|
|
/* autoconfig stuff */
|
|
static int rfc_match(struct device *, struct cfdata *, void *);
|
|
static void rfc_attach(struct device *, struct device *, void *);
|
|
static int rf_match(struct device *, struct cfdata *, void *);
|
|
static void rf_attach(struct device *, struct device *, void *);
|
|
static int rf_print(void *, const char *);
|
|
|
|
/* device interface functions / interface to disk(9) */
|
|
dev_type_open(rfopen);
|
|
dev_type_close(rfclose);
|
|
dev_type_read(rfread);
|
|
dev_type_write(rfwrite);
|
|
dev_type_ioctl(rfioctl);
|
|
dev_type_strategy(rfstrategy);
|
|
dev_type_dump(rfdump);
|
|
dev_type_size(rfsize);
|
|
|
|
|
|
/* Entries in block and character major device number switch table. */
|
|
const struct bdevsw rf_bdevsw = {
|
|
rfopen,
|
|
rfclose,
|
|
rfstrategy,
|
|
rfioctl,
|
|
rfdump,
|
|
rfsize,
|
|
D_DISK
|
|
};
|
|
|
|
const struct cdevsw rf_cdevsw = {
|
|
rfopen,
|
|
rfclose,
|
|
rfread,
|
|
rfwrite,
|
|
rfioctl,
|
|
nostop,
|
|
notty,
|
|
nopoll,
|
|
nommap,
|
|
nokqfilter,
|
|
D_DISK
|
|
};
|
|
|
|
|
|
|
|
struct rfc_softc {
|
|
struct device sc_dev; /* common device data */
|
|
struct device *sc_childs[2]; /* child devices */
|
|
struct evcnt sc_intr_count; /* Interrupt counter for statistics */
|
|
struct buf *sc_curbuf; /* buf that is currently in work */
|
|
bus_space_tag_t sc_iot; /* bus_space I/O tag */
|
|
bus_space_handle_t sc_ioh; /* bus_space I/O handle */
|
|
bus_dma_tag_t sc_dmat; /* bus_dma DMA tag */
|
|
bus_dmamap_t sc_dmam; /* bus_dma DMA map */
|
|
void *sc_bufidx; /* current position in buffer data */
|
|
int sc_curchild; /* child whos bufq is in work */
|
|
int sc_bytesleft; /* bytes left to transfer */
|
|
u_int8_t type; /* controller type, 1 or 2 */
|
|
};
|
|
|
|
|
|
|
|
CFATTACH_DECL(
|
|
rfc,
|
|
sizeof(struct rfc_softc),
|
|
rfc_match,
|
|
rfc_attach,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
|
|
|
|
struct rf_softc {
|
|
struct device sc_dev; /* common device data */
|
|
struct disk sc_disk; /* common disk device data */
|
|
struct bufq_state *sc_bufq; /* queue of pending transfers */
|
|
int sc_state; /* state of drive */
|
|
u_int8_t sc_dnum; /* drive number, 0 or 1 */
|
|
};
|
|
|
|
|
|
|
|
CFATTACH_DECL(
|
|
rf,
|
|
sizeof(struct rf_softc),
|
|
rf_match,
|
|
rf_attach,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
|
|
|
|
struct rfc_attach_args {
|
|
u_int8_t type; /* controller type, 1 or 2 */
|
|
u_int8_t dnum; /* drive number, 0 or 1 */
|
|
};
|
|
|
|
|
|
|
|
struct dkdriver rfdkdriver = {
|
|
rfstrategy
|
|
};
|
|
|
|
|
|
|
|
/* helper functions */
|
|
int rfc_sendcmd(struct rfc_softc *, int, int, int);
|
|
struct rf_softc* get_new_buf( struct rfc_softc *);
|
|
static void rfc_intr(void *);
|
|
|
|
|
|
|
|
/*
|
|
* Issue a reset command to the controller and look for the bits in
|
|
* RX2CS and RX2ES.
|
|
* RX2CS_RX02 and / or RX2CS_DD can be set,
|
|
* RX2ES has to be set, all other bits must be 0
|
|
*/
|
|
int
|
|
rfc_match(struct device *parent, struct cfdata *match, void *aux)
|
|
{
|
|
struct uba_attach_args *ua = aux;
|
|
int i;
|
|
|
|
/* Issue reset command. */
|
|
bus_space_write_2(ua->ua_iot, ua->ua_ioh, RX2CS, RX2CS_INIT);
|
|
/* Wait for the controller to become ready, that is when
|
|
* RX2CS_DONE, RX2ES_RDY and RX2ES_ID are set. */
|
|
for (i = 0 ; i < 20 ; i++) {
|
|
if ((bus_space_read_2(ua->ua_iot, ua->ua_ioh, RX2CS)
|
|
& RX2CS_DONE) != 0
|
|
&& (bus_space_read_2(ua->ua_iot, ua->ua_ioh, RX2ES)
|
|
& (RX2ES_RDY | RX2ES_ID)) != 0)
|
|
break;
|
|
DELAY(100000); /* wait 100ms */
|
|
}
|
|
/*
|
|
* Give up if the timeout has elapsed
|
|
* and the controller is not ready.
|
|
*/
|
|
if (i >= 20)
|
|
return(0);
|
|
/*
|
|
* Issue a Read Status command with interrupt enabled.
|
|
* The uba(4) driver wants to catch the interrupt to get the
|
|
* interrupt vector and level of the device
|
|
*/
|
|
bus_space_write_2(ua->ua_iot, ua->ua_ioh, RX2CS,
|
|
RX2CS_RSTAT | RX2CS_IE);
|
|
/*
|
|
* Wait for command to finish, ignore errors and
|
|
* abort if the controller does not respond within the timeout
|
|
*/
|
|
for (i = 0 ; i < 20 ; i++) {
|
|
if ((bus_space_read_2(ua->ua_iot, ua->ua_ioh, RX2CS)
|
|
& (RX2CS_DONE | RX2CS_IE)) != 0
|
|
&& (bus_space_read_2(ua->ua_iot, ua->ua_ioh, RX2ES)
|
|
& RX2ES_RDY) != 0 )
|
|
return(1);
|
|
DELAY(100000); /* wait 100ms */
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
|
|
|
|
/* #define RX02_PROBE 1 */
|
|
#ifdef RX02_PROBE
|
|
/*
|
|
* Probe the density of an inserted floppy disk.
|
|
* This is done by reading a sector from disk.
|
|
* Return -1 on error, 0 on SD and 1 on DD.
|
|
*/
|
|
int rfcprobedens(struct rfc_softc *, int);
|
|
int
|
|
rfcprobedens(struct rfc_softc *rfc_sc, int dnum)
|
|
{
|
|
int dens_flag;
|
|
int i;
|
|
|
|
dens_flag = 0;
|
|
do {
|
|
bus_space_write_2(rfc_sc->sc_iot, rfc_sc->sc_ioh, RX2CS,
|
|
RX2CS_RSEC | (dens_flag == 0 ? 0 : RX2CS_DD)
|
|
| (dnum == 0 ? 0 : RX2CS_US));
|
|
/*
|
|
* Transfer request set?
|
|
* Wait 50us, the controller needs this time to setle
|
|
*/
|
|
DELAY(50);
|
|
if ((bus_space_read_2(rfc_sc->sc_iot, rfc_sc->sc_ioh, RX2CS)
|
|
& RX2CS_TR) == 0) {
|
|
printf("%s: did not respond to Read Sector CMD(1)\n",
|
|
rfc_sc->sc_dev.dv_xname);
|
|
return(-1);
|
|
}
|
|
bus_space_write_2(rfc_sc->sc_iot, rfc_sc->sc_ioh, RX2SA, 1);
|
|
/* Wait 50us, the controller needs this time to setle */
|
|
DELAY(50);
|
|
if ((bus_space_read_2(rfc_sc->sc_iot, rfc_sc->sc_ioh, RX2CS)
|
|
& RX2CS_TR) == 0) {
|
|
printf("%s: did not respond to Read Sector CMD(2)\n",
|
|
rfc_sc->sc_dev.dv_xname);
|
|
return(-1);
|
|
}
|
|
bus_space_write_2(rfc_sc->sc_iot, rfc_sc->sc_ioh, RX2TA, 1);
|
|
/* Wait for the command to finish */
|
|
for (i = 0 ; i < 200 ; i++) {
|
|
if ((bus_space_read_2(rfc_sc->sc_iot, rfc_sc->sc_ioh,
|
|
RX2CS) & RX2CS_DONE) != 0)
|
|
break;
|
|
DELAY(10000); /* wait 10ms */
|
|
}
|
|
if (i >= 200) {
|
|
printf("%s: did not respond to Read Sector CMD(3)\n",
|
|
rfc_sc->sc_dev.dv_xname);
|
|
return(-1);
|
|
}
|
|
if ((bus_space_read_2(rfc_sc->sc_iot, rfc_sc->sc_ioh, RX2CS)
|
|
& RX2CS_ERR) == 0)
|
|
return(dens_flag);
|
|
} while (rfc_sc->type == 2 && dens_flag++ == 0);
|
|
return(-1);
|
|
}
|
|
#endif /* RX02_PROBE */
|
|
|
|
|
|
|
|
void
|
|
rfc_attach(struct device *parent, struct device *self, void *aux)
|
|
{
|
|
struct rfc_softc *rfc_sc = device_private(self);
|
|
struct uba_attach_args *ua = aux;
|
|
struct rfc_attach_args rfc_aa;
|
|
int i;
|
|
|
|
rfc_sc->sc_iot = ua->ua_iot;
|
|
rfc_sc->sc_ioh = ua->ua_ioh;
|
|
rfc_sc->sc_dmat = ua->ua_dmat;
|
|
rfc_sc->sc_curbuf = NULL;
|
|
/* Tell the QBus busdriver about our interrupt handler. */
|
|
uba_intr_establish(ua->ua_icookie, ua->ua_cvec, rfc_intr, rfc_sc,
|
|
&rfc_sc->sc_intr_count);
|
|
/* Attach to the interrupt counter, see evcnt(9) */
|
|
evcnt_attach_dynamic(&rfc_sc->sc_intr_count, EVCNT_TYPE_INTR,
|
|
ua->ua_evcnt, rfc_sc->sc_dev.dv_xname, "intr");
|
|
/* get a bus_dma(9) handle */
|
|
i = bus_dmamap_create(rfc_sc->sc_dmat, RX2_BYTE_DD, 1, RX2_BYTE_DD, 0,
|
|
BUS_DMA_ALLOCNOW, &rfc_sc->sc_dmam);
|
|
if (i != 0) {
|
|
printf("rfc_attach: Error creating bus dma map: %d\n", i);
|
|
return;
|
|
}
|
|
|
|
/* Issue reset command. */
|
|
bus_space_write_2(rfc_sc->sc_iot, rfc_sc->sc_ioh, RX2CS, RX2CS_INIT);
|
|
/*
|
|
* Wait for the controller to become ready, that is when
|
|
* RX2CS_DONE, RX2ES_RDY and RX2ES_ID are set.
|
|
*/
|
|
for (i = 0 ; i < 20 ; i++) {
|
|
if ((bus_space_read_2(rfc_sc->sc_iot, rfc_sc->sc_ioh, RX2CS)
|
|
& RX2CS_DONE) != 0
|
|
&& (bus_space_read_2(rfc_sc->sc_iot, rfc_sc->sc_ioh, RX2ES)
|
|
& (RX2ES_RDY | RX2ES_ID)) != 0)
|
|
break;
|
|
DELAY(100000); /* wait 100ms */
|
|
}
|
|
/*
|
|
* Give up if the timeout has elapsed
|
|
* and the controller is not ready.
|
|
*/
|
|
if (i >= 20) {
|
|
printf(": did not respond to INIT CMD\n");
|
|
return;
|
|
}
|
|
/* Is ths a RX01 or a RX02? */
|
|
if ((bus_space_read_2(rfc_sc->sc_iot, rfc_sc->sc_ioh, RX2CS)
|
|
& RX2CS_RX02) != 0) {
|
|
rfc_sc->type = 2;
|
|
rfc_aa.type = 2;
|
|
} else {
|
|
rfc_sc->type = 1;
|
|
rfc_aa.type = 1;
|
|
}
|
|
printf(": RX0%d\n", rfc_sc->type);
|
|
|
|
#ifndef RX02_PROBE
|
|
/*
|
|
* Bouth disk drievs and the controller are one physical unit.
|
|
* If we found the controller, there will be bouth disk drievs.
|
|
* So attach them.
|
|
*/
|
|
rfc_aa.dnum = 0;
|
|
rfc_sc->sc_childs[0] = config_found(&rfc_sc->sc_dev, &rfc_aa,rf_print);
|
|
rfc_aa.dnum = 1;
|
|
rfc_sc->sc_childs[1] = config_found(&rfc_sc->sc_dev, &rfc_aa,rf_print);
|
|
#else /* RX02_PROBE */
|
|
/*
|
|
* There are clones of the DEC RX system with standard shugart
|
|
* interface. In this case we can not be sure that there are
|
|
* bouth disk drievs. So we want to do a detection of attached
|
|
* drives. This is done by reading a sector from disk. This means
|
|
* that there must be a formatted disk in the drive at boot time.
|
|
* This is bad, but I did not find another way to detect the
|
|
* (non)existence of a floppy drive.
|
|
*/
|
|
if (rfcprobedens(rfc_sc, 0) >= 0) {
|
|
rfc_aa.dnum = 0;
|
|
rfc_sc->sc_childs[0] = config_found(&rfc_sc->sc_dev, &rfc_aa,
|
|
rf_print);
|
|
} else
|
|
rfc_sc->sc_childs[0] = NULL;
|
|
if (rfcprobedens(rfc_sc, 1) >= 0) {
|
|
rfc_aa.dnum = 1;
|
|
rfc_sc->sc_childs[1] = config_found(&rfc_sc->sc_dev, &rfc_aa,
|
|
rf_print);
|
|
} else
|
|
rfc_sc->sc_childs[1] = NULL;
|
|
#endif /* RX02_PROBE */
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
int
|
|
rf_match(struct device *parent, struct cfdata *match, void *aux)
|
|
{
|
|
struct rfc_attach_args *rfc_aa = aux;
|
|
|
|
/*
|
|
* Only attach if the locator is wildcarded or
|
|
* if the specified locator addresses the current device.
|
|
*/
|
|
if (match->cf_loc[RFCCF_DRIVE] == RFCCF_DRIVE_DEFAULT ||
|
|
match->cf_loc[RFCCF_DRIVE] == rfc_aa->dnum)
|
|
return(1);
|
|
return(0);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
rf_attach(struct device *parent, struct device *self, void *aux)
|
|
{
|
|
struct rf_softc *rf_sc = device_private(self);
|
|
struct rfc_attach_args *rfc_aa = (struct rfc_attach_args *)aux;
|
|
struct rfc_softc *rfc_sc;
|
|
struct disklabel *dl;
|
|
|
|
rfc_sc = (struct rfc_softc *)device_parent(&rf_sc->sc_dev);
|
|
rf_sc->sc_dnum = rfc_aa->dnum;
|
|
rf_sc->sc_state = 0;
|
|
disk_init(&rf_sc->sc_disk, rf_sc->sc_dev.dv_xname, &rfdkdriver);
|
|
disk_attach(&rf_sc->sc_disk);
|
|
dl = rf_sc->sc_disk.dk_label;
|
|
dl->d_type = DTYPE_FLOPPY; /* drive type */
|
|
dl->d_magic = DISKMAGIC; /* the magic number */
|
|
dl->d_magic2 = DISKMAGIC;
|
|
dl->d_typename[0] = 'R';
|
|
dl->d_typename[1] = 'X';
|
|
dl->d_typename[2] = '0';
|
|
dl->d_typename[3] = rfc_sc->type == 1 ? '1' : '2'; /* type name */
|
|
dl->d_typename[4] = '\0';
|
|
dl->d_secsize = DEV_BSIZE; /* bytes per sector */
|
|
/*
|
|
* Fill in some values to have a initialized data structure. Some
|
|
* values will be reset by rfopen() depending on the actual density.
|
|
*/
|
|
dl->d_nsectors = RX2_SECTORS; /* sectors per track */
|
|
dl->d_ntracks = 1; /* tracks per cylinder */
|
|
dl->d_ncylinders = RX2_TRACKS; /* cylinders per unit */
|
|
dl->d_secpercyl = RX2_SECTORS; /* sectors per cylinder */
|
|
dl->d_secperunit = RX2_SECTORS * RX2_TRACKS; /* sectors per unit */
|
|
dl->d_rpm = 360; /* rotational speed */
|
|
dl->d_interleave = 1; /* hardware sector interleave */
|
|
/* number of partitions in following */
|
|
dl->d_npartitions = MAXPARTITIONS;
|
|
dl->d_bbsize = 0; /* size of boot area at sn0, bytes */
|
|
dl->d_sbsize = 0; /* max size of fs superblock, bytes */
|
|
/* number of sectors in partition */
|
|
dl->d_partitions[0].p_size = 501;
|
|
dl->d_partitions[0].p_offset = 0; /* starting sector */
|
|
dl->d_partitions[0].p_fsize = 0; /* fs basic fragment size */
|
|
dl->d_partitions[0].p_fstype = 0; /* fs type */
|
|
dl->d_partitions[0].p_frag = 0; /* fs fragments per block */
|
|
dl->d_partitions[1].p_size = RX2_SECTORS * RX2_TRACKS / 2;
|
|
dl->d_partitions[1].p_offset = 0; /* starting sector */
|
|
dl->d_partitions[1].p_fsize = 0; /* fs basic fragment size */
|
|
dl->d_partitions[1].p_fstype = 0; /* fs type */
|
|
dl->d_partitions[1].p_frag = 0; /* fs fragments per block */
|
|
dl->d_partitions[2].p_size = RX2_SECTORS * RX2_TRACKS;
|
|
dl->d_partitions[2].p_offset = 0; /* starting sector */
|
|
dl->d_partitions[2].p_fsize = 0; /* fs basic fragment size */
|
|
dl->d_partitions[2].p_fstype = 0; /* fs type */
|
|
dl->d_partitions[2].p_frag = 0; /* fs fragments per block */
|
|
bufq_alloc(&rf_sc->sc_bufq, "disksort", BUFQ_SORT_CYLINDER);
|
|
printf("\n");
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
int
|
|
rf_print(void *aux, const char *name)
|
|
{
|
|
struct rfc_attach_args *rfc_aa = aux;
|
|
|
|
if (name != NULL)
|
|
aprint_normal("RX0%d at %s", rfc_aa->type, name);
|
|
aprint_normal(" drive %d", rfc_aa->dnum);
|
|
return(UNCONF);
|
|
}
|
|
|
|
|
|
|
|
/* Send a command to the controller */
|
|
int
|
|
rfc_sendcmd(struct rfc_softc *rfc_sc, int cmd, int data1, int data2)
|
|
{
|
|
|
|
/* Write command to CSR. */
|
|
bus_space_write_2(rfc_sc->sc_iot, rfc_sc->sc_ioh, RX2CS, cmd);
|
|
/* Wait 50us, the controller needs this time to setle. */
|
|
DELAY(50);
|
|
/* Write parameter 1 to DBR */
|
|
if ((cmd & RX2CS_FC) != RX2CS_RSTAT) {
|
|
/* Transfer request set? */
|
|
if ((bus_space_read_2(rfc_sc->sc_iot, rfc_sc->sc_ioh, RX2CS)
|
|
& RX2CS_TR) == 0) {
|
|
printf("%s: did not respond to CMD %x (1)\n",
|
|
rfc_sc->sc_dev.dv_xname, cmd);
|
|
return(-1);
|
|
}
|
|
bus_space_write_2(rfc_sc->sc_iot, rfc_sc->sc_ioh, RX2DB,
|
|
data1);
|
|
}
|
|
/* Write parameter 2 to DBR */
|
|
if ((cmd & RX2CS_FC) <= RX2CS_RSEC || (cmd & RX2CS_FC) == RX2CS_WDDS) {
|
|
/* Wait 50us, the controller needs this time to setle. */
|
|
DELAY(50);
|
|
/* Transfer request set? */
|
|
if ((bus_space_read_2(rfc_sc->sc_iot, rfc_sc->sc_ioh, RX2CS)
|
|
& RX2CS_TR) == 0) {
|
|
printf("%s: did not respond to CMD %x (2)\n",
|
|
rfc_sc->sc_dev.dv_xname, cmd);
|
|
return(-1);
|
|
}
|
|
bus_space_write_2(rfc_sc->sc_iot, rfc_sc->sc_ioh, RX2DB,
|
|
data2);
|
|
}
|
|
return(1);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
rfstrategy(struct buf *buf)
|
|
{
|
|
struct rf_softc *rf_sc;
|
|
struct rfc_softc *rfc_sc;
|
|
int i;
|
|
|
|
i = DISKUNIT(buf->b_dev);
|
|
if (i >= rf_cd.cd_ndevs || (rf_sc = rf_cd.cd_devs[i]) == NULL) {
|
|
buf->b_error = ENXIO;
|
|
biodone(buf);
|
|
return;
|
|
}
|
|
rfc_sc = (struct rfc_softc *)device_parent(&rf_sc->sc_dev);
|
|
/* We are going to operate on a non-open dev? PANIC! */
|
|
if ((rf_sc->sc_state & 1 << (DISKPART(buf->b_dev) + RFS_OPEN_SHIFT))
|
|
== 0)
|
|
panic("rfstrategy: can not operate on non-open drive %s "
|
|
"partition %d", rf_sc->sc_dev.dv_xname,
|
|
DISKPART(buf->b_dev));
|
|
if (buf->b_bcount == 0) {
|
|
biodone(buf);
|
|
return;
|
|
}
|
|
/*
|
|
* BUFQ_PUT() operates on b_rawblkno. rfstrategy() gets
|
|
* only b_blkno that is partition relative. As a floppy does not
|
|
* have partitions b_rawblkno == b_blkno.
|
|
*/
|
|
buf->b_rawblkno = buf->b_blkno;
|
|
/*
|
|
* from sys/kern/subr_disk.c:
|
|
* Seek sort for disks. We depend on the driver which calls us using
|
|
* b_resid as the current cylinder number.
|
|
*/
|
|
i = splbio();
|
|
if (rfc_sc->sc_curbuf == NULL) {
|
|
rfc_sc->sc_curchild = rf_sc->sc_dnum;
|
|
rfc_sc->sc_curbuf = buf;
|
|
rfc_sc->sc_bufidx = buf->b_un.b_addr;
|
|
rfc_sc->sc_bytesleft = buf->b_bcount;
|
|
rfc_intr(rfc_sc);
|
|
} else {
|
|
buf->b_resid = buf->b_blkno / RX2_SECTORS;
|
|
BUFQ_PUT(rf_sc->sc_bufq, buf);
|
|
buf->b_resid = 0;
|
|
}
|
|
splx(i);
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Look if there is another buffer in the bufferqueue of this drive
|
|
* and start to process it if there is one.
|
|
* If the bufferqueue is empty, look at the bufferqueue of the other drive
|
|
* that is attached to this controller.
|
|
* Start procesing the bufferqueue of the other drive if it isn't empty.
|
|
* Return a pointer to the softc structure of the drive that is now
|
|
* ready to process a buffer or NULL if there is no buffer in either queues.
|
|
*/
|
|
struct rf_softc*
|
|
get_new_buf( struct rfc_softc *rfc_sc)
|
|
{
|
|
struct rf_softc *rf_sc;
|
|
struct rf_softc *other_drive;
|
|
|
|
rf_sc = (struct rf_softc *)rfc_sc->sc_childs[rfc_sc->sc_curchild];
|
|
rfc_sc->sc_curbuf = BUFQ_GET(rf_sc->sc_bufq);
|
|
if (rfc_sc->sc_curbuf != NULL) {
|
|
rfc_sc->sc_bufidx = rfc_sc->sc_curbuf->b_un.b_addr;
|
|
rfc_sc->sc_bytesleft = rfc_sc->sc_curbuf->b_bcount;
|
|
} else {
|
|
RFS_SETCMD(rf_sc->sc_state, RFS_IDLE);
|
|
other_drive = (struct rf_softc *)
|
|
rfc_sc->sc_childs[ rfc_sc->sc_curchild == 0 ? 1 : 0];
|
|
if (other_drive != NULL
|
|
&& BUFQ_PEEK(other_drive->sc_bufq) != NULL) {
|
|
rfc_sc->sc_curchild = rfc_sc->sc_curchild == 0 ? 1 : 0;
|
|
rf_sc = other_drive;
|
|
rfc_sc->sc_curbuf = BUFQ_GET(rf_sc->sc_bufq);
|
|
rfc_sc->sc_bufidx = rfc_sc->sc_curbuf->b_un.b_addr;
|
|
rfc_sc->sc_bytesleft = rfc_sc->sc_curbuf->b_bcount;
|
|
} else
|
|
return(NULL);
|
|
}
|
|
return(rf_sc);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
rfc_intr(void *intarg)
|
|
{
|
|
struct rfc_softc *rfc_sc = intarg;
|
|
struct rf_softc *rf_sc;
|
|
int i;
|
|
|
|
rf_sc = (struct rf_softc *)rfc_sc->sc_childs[rfc_sc->sc_curchild];
|
|
do {
|
|
/*
|
|
* First clean up from previous command...
|
|
*/
|
|
switch (rf_sc->sc_state & RFS_CMDS) {
|
|
case RFS_PROBING: /* density detect / verify started */
|
|
disk_unbusy(&rf_sc->sc_disk, 0, 1);
|
|
if ((bus_space_read_2(rfc_sc->sc_iot, rfc_sc->sc_ioh,
|
|
RX2CS) & RX2CS_ERR) == 0) {
|
|
RFS_SETCMD(rf_sc->sc_state, RFS_IDLE);
|
|
wakeup(rf_sc);
|
|
} else {
|
|
if (rfc_sc->type == 2
|
|
&& (rf_sc->sc_state & RFS_DENS) == 0
|
|
&& (rf_sc->sc_state & RFS_AD) != 0) {
|
|
/* retry at DD */
|
|
rf_sc->sc_state |= RFS_DENS;
|
|
disk_busy(&rf_sc->sc_disk);
|
|
if (rfc_sendcmd(rfc_sc, RX2CS_RSEC
|
|
| RX2CS_IE | RX2CS_DD |
|
|
(rf_sc->sc_dnum == 0 ? 0 :
|
|
RX2CS_US), 1, 1) < 0) {
|
|
disk_unbusy(&rf_sc->sc_disk,
|
|
0, 1);
|
|
RFS_SETCMD(rf_sc->sc_state,
|
|
RFS_NOTINIT);
|
|
wakeup(rf_sc);
|
|
}
|
|
} else {
|
|
printf("%s: density error.\n",
|
|
rf_sc->sc_dev.dv_xname);
|
|
RFS_SETCMD(rf_sc->sc_state,RFS_NOTINIT);
|
|
wakeup(rf_sc);
|
|
}
|
|
}
|
|
return;
|
|
case RFS_IDLE: /* controller is idle */
|
|
if (rfc_sc->sc_curbuf->b_bcount
|
|
% ((rf_sc->sc_state & RFS_DENS) == 0
|
|
? RX2_BYTE_SD : RX2_BYTE_DD) != 0) {
|
|
/*
|
|
* can only handle blocks that are a multiple
|
|
* of the physical block size
|
|
*/
|
|
rfc_sc->sc_curbuf->b_error = EIO;
|
|
}
|
|
RFS_SETCMD(rf_sc->sc_state, (rfc_sc->sc_curbuf->b_flags
|
|
& B_READ) != 0 ? RFS_RSEC : RFS_FBUF);
|
|
break;
|
|
case RFS_RSEC: /* Read Sector */
|
|
disk_unbusy(&rf_sc->sc_disk, 0, 1);
|
|
/* check for errors */
|
|
if ((bus_space_read_2(rfc_sc->sc_iot, rfc_sc->sc_ioh,
|
|
RX2CS) & RX2CS_ERR) != 0) {
|
|
/* should do more verbose error reporting */
|
|
printf("rfc_intr: Error reading secotr: %x\n",
|
|
bus_space_read_2(rfc_sc->sc_iot,
|
|
rfc_sc->sc_ioh, RX2ES) );
|
|
rfc_sc->sc_curbuf->b_error = EIO;
|
|
}
|
|
RFS_SETCMD(rf_sc->sc_state, RFS_EBUF);
|
|
break;
|
|
case RFS_WSEC: /* Write Sector */
|
|
i = (rf_sc->sc_state & RFS_DENS) == 0
|
|
? RX2_BYTE_SD : RX2_BYTE_DD;
|
|
disk_unbusy(&rf_sc->sc_disk, i, 0);
|
|
/* check for errors */
|
|
if ((bus_space_read_2(rfc_sc->sc_iot, rfc_sc->sc_ioh,
|
|
RX2CS) & RX2CS_ERR) != 0) {
|
|
/* should do more verbose error reporting */
|
|
printf("rfc_intr: Error writing secotr: %x\n",
|
|
bus_space_read_2(rfc_sc->sc_iot,
|
|
rfc_sc->sc_ioh, RX2ES) );
|
|
rfc_sc->sc_curbuf->b_error = EIO;
|
|
break;
|
|
}
|
|
if (rfc_sc->sc_bytesleft > i) {
|
|
rfc_sc->sc_bytesleft -= i;
|
|
rfc_sc->sc_bufidx =
|
|
(char *)rfc_sc->sc_bufidx + i;
|
|
} else {
|
|
biodone(rfc_sc->sc_curbuf);
|
|
rf_sc = get_new_buf( rfc_sc);
|
|
if (rf_sc == NULL)
|
|
return;
|
|
}
|
|
RFS_SETCMD(rf_sc->sc_state,
|
|
(rfc_sc->sc_curbuf->b_flags & B_READ) != 0
|
|
? RFS_RSEC : RFS_FBUF);
|
|
break;
|
|
case RFS_FBUF: /* Fill Buffer */
|
|
disk_unbusy(&rf_sc->sc_disk, 0, 0);
|
|
bus_dmamap_unload(rfc_sc->sc_dmat, rfc_sc->sc_dmam);
|
|
/* check for errors */
|
|
if ((bus_space_read_2(rfc_sc->sc_iot, rfc_sc->sc_ioh,
|
|
RX2CS) & RX2CS_ERR) != 0) {
|
|
/* should do more verbose error reporting */
|
|
printf("rfc_intr: Error while DMA: %x\n",
|
|
bus_space_read_2(rfc_sc->sc_iot,
|
|
rfc_sc->sc_ioh, RX2ES));
|
|
rfc_sc->sc_curbuf->b_error = EIO;
|
|
}
|
|
RFS_SETCMD(rf_sc->sc_state, RFS_WSEC);
|
|
break;
|
|
case RFS_EBUF: /* Empty Buffer */
|
|
i = (rf_sc->sc_state & RFS_DENS) == 0
|
|
? RX2_BYTE_SD : RX2_BYTE_DD;
|
|
disk_unbusy(&rf_sc->sc_disk, i, 1);
|
|
bus_dmamap_unload(rfc_sc->sc_dmat, rfc_sc->sc_dmam);
|
|
/* check for errors */
|
|
if ((bus_space_read_2(rfc_sc->sc_iot, rfc_sc->sc_ioh,
|
|
RX2CS) & RX2CS_ERR) != 0) {
|
|
/* should do more verbose error reporting */
|
|
printf("rfc_intr: Error while DMA: %x\n",
|
|
bus_space_read_2(rfc_sc->sc_iot,
|
|
rfc_sc->sc_ioh, RX2ES));
|
|
rfc_sc->sc_curbuf->b_error = EIO;
|
|
break;
|
|
}
|
|
if (rfc_sc->sc_bytesleft > i) {
|
|
rfc_sc->sc_bytesleft -= i;
|
|
rfc_sc->sc_bufidx =
|
|
(char *)rfc_sc->sc_bufidx + i;
|
|
} else {
|
|
biodone(rfc_sc->sc_curbuf);
|
|
rf_sc = get_new_buf( rfc_sc);
|
|
if (rf_sc == NULL)
|
|
return;
|
|
}
|
|
RFS_SETCMD(rf_sc->sc_state,
|
|
(rfc_sc->sc_curbuf->b_flags & B_READ) != 0
|
|
? RFS_RSEC : RFS_FBUF);
|
|
break;
|
|
case RFS_NOTINIT: /* Device is not open */
|
|
case RFS_SMD: /* Set Media Density */
|
|
case RFS_RSTAT: /* Read Status */
|
|
case RFS_WDDS: /* Write Deleted Data Sector */
|
|
case RFS_REC: /* Read Error Code */
|
|
default:
|
|
panic("Impossible state in rfc_intr(1).\n");
|
|
}
|
|
|
|
if (rfc_sc->sc_curbuf->b_error != 0) {
|
|
/*
|
|
* An error occurred while processing this buffer.
|
|
* Finish it and try to get a new buffer to process.
|
|
* Return if there are no buffers in the queues.
|
|
* This loops until the queues are empty or a new
|
|
* action was successfully scheduled.
|
|
*/
|
|
rfc_sc->sc_curbuf->b_resid = rfc_sc->sc_bytesleft;
|
|
rfc_sc->sc_curbuf->b_error = EIO;
|
|
biodone(rfc_sc->sc_curbuf);
|
|
rf_sc = get_new_buf( rfc_sc);
|
|
if (rf_sc == NULL)
|
|
return;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* ... then initiate next command.
|
|
*/
|
|
switch (rf_sc->sc_state & RFS_CMDS) {
|
|
case RFS_EBUF: /* Empty Buffer */
|
|
i = bus_dmamap_load(rfc_sc->sc_dmat, rfc_sc->sc_dmam,
|
|
rfc_sc->sc_bufidx, (rf_sc->sc_state & RFS_DENS) == 0
|
|
? RX2_BYTE_SD : RX2_BYTE_DD,
|
|
rfc_sc->sc_curbuf->b_proc, BUS_DMA_NOWAIT);
|
|
if (i != 0) {
|
|
printf("rfc_intr: Error loading dmamap: %d\n",
|
|
i);
|
|
rfc_sc->sc_curbuf->b_error = EIO;
|
|
break;
|
|
}
|
|
disk_busy(&rf_sc->sc_disk);
|
|
if (rfc_sendcmd(rfc_sc, RX2CS_EBUF | RX2CS_IE
|
|
| ((rf_sc->sc_state & RFS_DENS) == 0 ? 0 : RX2CS_DD)
|
|
| (rf_sc->sc_dnum == 0 ? 0 : RX2CS_US)
|
|
| ((rfc_sc->sc_dmam->dm_segs[0].ds_addr
|
|
& 0x30000) >>4), ((rf_sc->sc_state & RFS_DENS) == 0
|
|
? RX2_BYTE_SD : RX2_BYTE_DD) / 2,
|
|
rfc_sc->sc_dmam->dm_segs[0].ds_addr & 0xffff) < 0) {
|
|
disk_unbusy(&rf_sc->sc_disk, 0, 1);
|
|
rfc_sc->sc_curbuf->b_error = EIO;
|
|
bus_dmamap_unload(rfc_sc->sc_dmat,
|
|
rfc_sc->sc_dmam);
|
|
}
|
|
break;
|
|
case RFS_FBUF: /* Fill Buffer */
|
|
i = bus_dmamap_load(rfc_sc->sc_dmat, rfc_sc->sc_dmam,
|
|
rfc_sc->sc_bufidx, (rf_sc->sc_state & RFS_DENS) == 0
|
|
? RX2_BYTE_SD : RX2_BYTE_DD,
|
|
rfc_sc->sc_curbuf->b_proc, BUS_DMA_NOWAIT);
|
|
if (i != 0) {
|
|
printf("rfc_intr: Error loading dmamap: %d\n",
|
|
i);
|
|
rfc_sc->sc_curbuf->b_error = EIO;
|
|
break;
|
|
}
|
|
disk_busy(&rf_sc->sc_disk);
|
|
if (rfc_sendcmd(rfc_sc, RX2CS_FBUF | RX2CS_IE
|
|
| ((rf_sc->sc_state & RFS_DENS) == 0 ? 0 : RX2CS_DD)
|
|
| (rf_sc->sc_dnum == 0 ? 0 : RX2CS_US)
|
|
| ((rfc_sc->sc_dmam->dm_segs[0].ds_addr
|
|
& 0x30000)>>4), ((rf_sc->sc_state & RFS_DENS) == 0
|
|
? RX2_BYTE_SD : RX2_BYTE_DD) / 2,
|
|
rfc_sc->sc_dmam->dm_segs[0].ds_addr & 0xffff) < 0) {
|
|
disk_unbusy(&rf_sc->sc_disk, 0, 0);
|
|
rfc_sc->sc_curbuf->b_error = EIO;
|
|
bus_dmamap_unload(rfc_sc->sc_dmat,
|
|
rfc_sc->sc_dmam);
|
|
}
|
|
break;
|
|
case RFS_WSEC: /* Write Sector */
|
|
i = (rfc_sc->sc_curbuf->b_bcount - rfc_sc->sc_bytesleft
|
|
+ rfc_sc->sc_curbuf->b_blkno * DEV_BSIZE) /
|
|
((rf_sc->sc_state & RFS_DENS) == 0
|
|
? RX2_BYTE_SD : RX2_BYTE_DD);
|
|
if (i > RX2_TRACKS * RX2_SECTORS) {
|
|
rfc_sc->sc_curbuf->b_error = EIO;
|
|
break;
|
|
}
|
|
disk_busy(&rf_sc->sc_disk);
|
|
if (rfc_sendcmd(rfc_sc, RX2CS_WSEC | RX2CS_IE
|
|
| (rf_sc->sc_dnum == 0 ? 0 : RX2CS_US)
|
|
| ((rf_sc->sc_state& RFS_DENS) == 0 ? 0 : RX2CS_DD),
|
|
i % RX2_SECTORS + 1, i / RX2_SECTORS) < 0) {
|
|
disk_unbusy(&rf_sc->sc_disk, 0, 0);
|
|
rfc_sc->sc_curbuf->b_error = EIO;
|
|
}
|
|
break;
|
|
case RFS_RSEC: /* Read Sector */
|
|
i = (rfc_sc->sc_curbuf->b_bcount - rfc_sc->sc_bytesleft
|
|
+ rfc_sc->sc_curbuf->b_blkno * DEV_BSIZE) /
|
|
((rf_sc->sc_state & RFS_DENS) == 0
|
|
? RX2_BYTE_SD : RX2_BYTE_DD);
|
|
if (i > RX2_TRACKS * RX2_SECTORS) {
|
|
rfc_sc->sc_curbuf->b_error = EIO;
|
|
break;
|
|
}
|
|
disk_busy(&rf_sc->sc_disk);
|
|
if (rfc_sendcmd(rfc_sc, RX2CS_RSEC | RX2CS_IE
|
|
| (rf_sc->sc_dnum == 0 ? 0 : RX2CS_US)
|
|
| ((rf_sc->sc_state& RFS_DENS) == 0 ? 0 : RX2CS_DD),
|
|
i % RX2_SECTORS + 1, i / RX2_SECTORS) < 0) {
|
|
disk_unbusy(&rf_sc->sc_disk, 0, 1);
|
|
rfc_sc->sc_curbuf->b_error = EIO;
|
|
}
|
|
break;
|
|
case RFS_NOTINIT: /* Device is not open */
|
|
case RFS_PROBING: /* density detect / verify started */
|
|
case RFS_IDLE: /* controller is idle */
|
|
case RFS_SMD: /* Set Media Density */
|
|
case RFS_RSTAT: /* Read Status */
|
|
case RFS_WDDS: /* Write Deleted Data Sector */
|
|
case RFS_REC: /* Read Error Code */
|
|
default:
|
|
panic("Impossible state in rfc_intr(2).\n");
|
|
}
|
|
|
|
if (rfc_sc->sc_curbuf->b_error != 0) {
|
|
/*
|
|
* An error occurred while processing this buffer.
|
|
* Finish it and try to get a new buffer to process.
|
|
* Return if there are no buffers in the queues.
|
|
* This loops until the queues are empty or a new
|
|
* action was successfully scheduled.
|
|
*/
|
|
rfc_sc->sc_curbuf->b_resid = rfc_sc->sc_bytesleft;
|
|
rfc_sc->sc_curbuf->b_error = EIO;
|
|
biodone(rfc_sc->sc_curbuf);
|
|
rf_sc = get_new_buf( rfc_sc);
|
|
if (rf_sc == NULL)
|
|
return;
|
|
continue;
|
|
}
|
|
} while ( 1 == 0 /* CONSTCOND */ );
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
int
|
|
rfdump(dev_t dev, daddr_t blkno, void *va, size_t size)
|
|
{
|
|
|
|
/* A 0.5MB floppy is much to small to take a system dump... */
|
|
return(ENXIO);
|
|
}
|
|
|
|
|
|
|
|
int
|
|
rfsize(dev_t dev)
|
|
{
|
|
|
|
return(-1);
|
|
}
|
|
|
|
|
|
|
|
int
|
|
rfopen(dev_t dev, int oflags, int devtype, struct lwp *l)
|
|
{
|
|
struct rf_softc *rf_sc;
|
|
struct rfc_softc *rfc_sc;
|
|
struct disklabel *dl;
|
|
int unit;
|
|
|
|
unit = DISKUNIT(dev);
|
|
if (unit >= rf_cd.cd_ndevs || (rf_sc = rf_cd.cd_devs[unit]) == NULL) {
|
|
return(ENXIO);
|
|
}
|
|
rfc_sc = (struct rfc_softc *)device_parent(&rf_sc->sc_dev);
|
|
dl = rf_sc->sc_disk.dk_label;
|
|
switch (DISKPART(dev)) {
|
|
case 0: /* Part. a is single density. */
|
|
/* opening in single and double density is senseless */
|
|
if ((rf_sc->sc_state & RFS_OPEN_B) != 0 )
|
|
return(ENXIO);
|
|
rf_sc->sc_state &= ~RFS_DENS;
|
|
rf_sc->sc_state &= ~RFS_AD;
|
|
rf_sc->sc_state |= RFS_OPEN_A;
|
|
break;
|
|
case 1: /* Part. b is double density. */
|
|
/*
|
|
* Opening a single density only drive in double
|
|
* density or simultaneous opening in single and
|
|
* double density is senseless.
|
|
*/
|
|
if (rfc_sc->type == 1
|
|
|| (rf_sc->sc_state & RFS_OPEN_A) != 0 )
|
|
return(ENXIO);
|
|
rf_sc->sc_state |= RFS_DENS;
|
|
rf_sc->sc_state &= ~RFS_AD;
|
|
rf_sc->sc_state |= RFS_OPEN_B;
|
|
break;
|
|
case 2: /* Part. c is auto density. */
|
|
rf_sc->sc_state |= RFS_AD;
|
|
rf_sc->sc_state |= RFS_OPEN_C;
|
|
break;
|
|
default:
|
|
return(ENXIO);
|
|
break;
|
|
}
|
|
if ((rf_sc->sc_state & RFS_CMDS) == RFS_NOTINIT) {
|
|
rfc_sc->sc_curchild = rf_sc->sc_dnum;
|
|
/*
|
|
* Controller is idle and density is not detected.
|
|
* Start a density probe by issuing a read sector command
|
|
* and sleep until the density probe finished.
|
|
* Due to this it is imposible to open unformatted media.
|
|
* As the RX02/02 is not able to format its own media,
|
|
* media must be purchased preformatted. fsck DEC makreting!
|
|
*/
|
|
RFS_SETCMD(rf_sc->sc_state, RFS_PROBING);
|
|
disk_busy(&rf_sc->sc_disk);
|
|
if (rfc_sendcmd(rfc_sc, RX2CS_RSEC | RX2CS_IE
|
|
| (rf_sc->sc_dnum == 0 ? 0 : RX2CS_US)
|
|
| ((rf_sc->sc_state & RFS_DENS) == 0 ? 0 : RX2CS_DD),
|
|
1, 1) < 0) {
|
|
rf_sc->sc_state = 0;
|
|
return(ENXIO);
|
|
}
|
|
/* wait max. 2 sec for density probe to finish */
|
|
if (tsleep(rf_sc, PRIBIO | PCATCH, "density probe", 2 * hz)
|
|
!= 0 || (rf_sc->sc_state & RFS_CMDS) == RFS_NOTINIT) {
|
|
/* timeout elapsed and / or something went wrong */
|
|
rf_sc->sc_state = 0;
|
|
return(ENXIO);
|
|
}
|
|
}
|
|
/* disklabel. We use different fake geometries for SD and DD. */
|
|
if ((rf_sc->sc_state & RFS_DENS) == 0) {
|
|
dl->d_nsectors = 10; /* sectors per track */
|
|
dl->d_secpercyl = 10; /* sectors per cylinder */
|
|
dl->d_ncylinders = 50; /* cylinders per unit */
|
|
dl->d_secperunit = 501; /* sectors per unit */
|
|
/* number of sectors in partition */
|
|
dl->d_partitions[2].p_size = 500;
|
|
} else {
|
|
dl->d_nsectors = RX2_SECTORS / 2; /* sectors per track */
|
|
dl->d_secpercyl = RX2_SECTORS / 2; /* sectors per cylinder */
|
|
dl->d_ncylinders = RX2_TRACKS; /* cylinders per unit */
|
|
/* sectors per unit */
|
|
dl->d_secperunit = RX2_SECTORS * RX2_TRACKS / 2;
|
|
/* number of sectors in partition */
|
|
dl->d_partitions[2].p_size = RX2_SECTORS * RX2_TRACKS / 2;
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
|
|
|
|
int
|
|
rfclose(dev_t dev, int fflag, int devtype, struct lwp *l)
|
|
{
|
|
struct rf_softc *rf_sc;
|
|
int unit;
|
|
|
|
unit = DISKUNIT(dev);
|
|
if (unit >= rf_cd.cd_ndevs || (rf_sc = rf_cd.cd_devs[unit]) == NULL) {
|
|
return(ENXIO);
|
|
}
|
|
if ((rf_sc->sc_state & 1 << (DISKPART(dev) + RFS_OPEN_SHIFT)) == 0)
|
|
panic("rfclose: can not close non-open drive %s "
|
|
"partition %d", rf_sc->sc_dev.dv_xname, DISKPART(dev));
|
|
else
|
|
rf_sc->sc_state &= ~(1 << (DISKPART(dev) + RFS_OPEN_SHIFT));
|
|
if ((rf_sc->sc_state & RFS_OPEN_MASK) == 0)
|
|
rf_sc->sc_state = 0;
|
|
return(0);
|
|
}
|
|
|
|
|
|
|
|
int
|
|
rfread(dev_t dev, struct uio *uio, int ioflag)
|
|
{
|
|
|
|
return(physio(rfstrategy, NULL, dev, B_READ, minphys, uio));
|
|
}
|
|
|
|
|
|
|
|
int
|
|
rfwrite(dev_t dev, struct uio *uio, int ioflag)
|
|
{
|
|
|
|
return(physio(rfstrategy, NULL, dev, B_WRITE, minphys, uio));
|
|
}
|
|
|
|
|
|
|
|
int
|
|
rfioctl(dev_t dev, u_long cmd, void *data, int fflag, struct lwp *l)
|
|
{
|
|
struct rf_softc *rf_sc;
|
|
int unit;
|
|
|
|
unit = DISKUNIT(dev);
|
|
if (unit >= rf_cd.cd_ndevs || (rf_sc = rf_cd.cd_devs[unit]) == NULL) {
|
|
return(ENXIO);
|
|
}
|
|
/* We are going to operate on a non-open dev? PANIC! */
|
|
if ((rf_sc->sc_state & 1 << (DISKPART(dev) + RFS_OPEN_SHIFT)) == 0)
|
|
panic("rfioctl: can not operate on non-open drive %s "
|
|
"partition %d", rf_sc->sc_dev.dv_xname, DISKPART(dev));
|
|
switch (cmd) {
|
|
/* get and set disklabel; DIOCGPART used internally */
|
|
case DIOCGDINFO: /* get */
|
|
memcpy(data, rf_sc->sc_disk.dk_label,
|
|
sizeof(struct disklabel));
|
|
return(0);
|
|
case DIOCSDINFO: /* set */
|
|
return(0);
|
|
case DIOCWDINFO: /* set, update disk */
|
|
return(0);
|
|
case DIOCGPART: /* get partition */
|
|
((struct partinfo *)data)->disklab = rf_sc->sc_disk.dk_label;
|
|
((struct partinfo *)data)->part =
|
|
&rf_sc->sc_disk.dk_label->d_partitions[DISKPART(dev)];
|
|
return(0);
|
|
|
|
/* do format operation, read or write */
|
|
case DIOCRFORMAT:
|
|
break;
|
|
case DIOCWFORMAT:
|
|
break;
|
|
|
|
case DIOCSSTEP: /* set step rate */
|
|
break;
|
|
case DIOCSRETRIES: /* set # of retries */
|
|
break;
|
|
case DIOCKLABEL: /* keep/drop label on close? */
|
|
break;
|
|
case DIOCWLABEL: /* write en/disable label */
|
|
break;
|
|
|
|
/* case DIOCSBAD: / * set kernel dkbad */
|
|
break; /* */
|
|
case DIOCEJECT: /* eject removable disk */
|
|
break;
|
|
case ODIOCEJECT: /* eject removable disk */
|
|
break;
|
|
case DIOCLOCK: /* lock/unlock pack */
|
|
break;
|
|
|
|
/* get default label, clear label */
|
|
case DIOCGDEFLABEL:
|
|
break;
|
|
case DIOCCLRLABEL:
|
|
break;
|
|
default:
|
|
return(ENOTTY);
|
|
}
|
|
|
|
return(ENOTTY);
|
|
}
|
|
|
|
|