/* $NetBSD: rf.c,v 1.37 2021/08/12 19:53:18 andvar 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.
*/
struct rfc_softc {
device_t sc_dev; /* common device data */
device_t 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 */
};
struct rf_softc {
device_t sc_dev; /* common device data */
struct disk sc_disk; /* common disk device data */
struct rfc_softc *sc_rfc; /* our parent */
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 */
};
/*
* 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(device_t parent, cfdata_t 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",
device_xname(rfc_sc->sc_dev));
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",
device_xname(rfc_sc->sc_dev));
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",
device_xname(rfc_sc->sc_dev));
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 */
rfc_sc->sc_dev = self;
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, device_xname(rfc_sc->sc_dev), "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
/*
* Both disk drives and the controller are one physical unit.
* If we found the controller, there will be both disk drives.
* So attach them.
*/
rfc_aa.dnum = 0;
rfc_sc->sc_childs[0] = config_found(rfc_sc->sc_dev, &rfc_aa, rf_print,
CFARGS_NONE);
rfc_aa.dnum = 1;
rfc_sc->sc_childs[1] = config_found(rfc_sc->sc_dev, &rfc_aa, rf_print,
CFARGS_NONE);
#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
* both disk drives. 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, CFARGS_NONE);
} 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, CFARGS_NONE);
} else
rfc_sc->sc_childs[1] = NULL;
#endif /* RX02_PROBE */
return;
}
/*
* 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);
}
if ((rf_sc = device_lookup_private(&rf_cd, DISKUNIT(buf->b_dev))) == NULL) {
buf->b_error = ENXIO;
biodone(buf);
return;
}
rfc_sc = rf_sc->sc_rfc;
/* 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 %"PRIu32, device_xname(rf_sc->sc_dev),
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.
*/
s = 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_data;
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(s);
}
/*
* 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 processing 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 = device_private(rfc_sc->sc_childs[rfc_sc->sc_curchild]);
for (;;) {
/*
* 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",
device_xname(rf_sc->sc_dev));
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): 0x%x\n",
rf_sc->sc_state & RFS_CMDS);
}
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): 0x%x\n",
rf_sc->sc_state & RFS_CMDS);
}
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;
}
break;
}
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;
if ((rf_sc = device_lookup_private(&rf_cd, DISKUNIT(dev))) == NULL)
return ENXIO;
rfc_sc = rf_sc->sc_rfc;
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 impossible to open unformatted media.
* As the RX02/02 is not able to format its own media,
* media must be purchased preformatted. fsck DEC marketing!
*/
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 = device_lookup_private(&rf_cd, DISKUNIT(dev));
if ((rf_sc->sc_state & 1 << (DISKPART(dev) + RFS_OPEN_SHIFT)) == 0)
panic("rfclose: can not close non-open drive %s "
"partition %"PRIu32, device_xname(rf_sc->sc_dev), 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)
{
int
rfioctl(dev_t dev, u_long cmd, void *data, int fflag, struct lwp *l)
{
struct rf_softc *rf_sc = device_lookup_private(&rf_cd, DISKUNIT(dev));
int error;
/* 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 %"PRIu32, device_xname(rf_sc->sc_dev), DISKPART(dev));
error = disk_ioctl(&rf_sc->sc_disk, dev, cmd, data, fflag, l);
if (error != EPASSTHROUGH)
return error;
switch (cmd) {
/* get and set disklabel; DIOCGPARTINFO used internally */
case DIOCSDINFO: /* set */
return(0);
case DIOCWDINFO: /* set, update disk */
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);
}