2021-10-11 18:37:13 -03:00

1120 lines
24 KiB
C

#ifndef lint
static char sccsid[] = "@(#)ip.c 1.1 94/10/31 Copyr 1985 Sun Micro";
#endif
/*
* Copyright (c) 1985 by Sun Microsystems, Inc.
*/
/*
* Driver for Interphase SMD-2180 controller.
*/
#include "ip.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/dk.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/dir.h>
#include <sys/user.h>
#include <sys/map.h>
#include <sys/vmmac.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <machine/psl.h>
#include <sun/dklabel.h>
#include <sun/dkio.h>
#include <sundev/mbvar.h>
#include <sundev/ipreg.h>
int ipprobe(), ipslave(), ipattach(), ipgo(), ipdone(), ippoll();
struct mb_ctlr *ipcinfo[NIPC];
struct mb_device *ipdinfo[NIP];
struct mb_driver ipcdriver = {
ipprobe, ipslave, ipattach, ipgo, ipdone, ippoll,
sizeof (struct ipdevice), "ip", ipdinfo, "ipc", ipcinfo, MDR_BIODMA,
};
#define NUNIT 8 /* max # of units per controller */
#define NLPART NDKMAP /* # of logical partitions (8) */
#define UNIT(dev) ((dev>>3) % NUNIT)
#define LPART(dev) (dev % NLPART)
#define SECSIZE 512
/*
* Definition of a unit
* (A unit is a physical partition of a drive,
* either the whole drive or the fixed or cartridge
* part of a Lark or the fixed head vs. moving head
* areas of a Fujitsu).
* Units with the 4 bit on are the funny ones.
*/
struct unit {
struct dk_map un_map[NLPART]; /* logical partitions */
char un_present; /* physical partition is present */
struct dk_geom un_g; /* disk geometry */
struct buf un_utab; /* for queuing per unit */
struct buf un_rtab; /* for raw I/O */
struct buf un_sbuf; /* for special commands */
u_char un_scmd; /* the special command */
u_char un_serr; /* the error from special command */
u_char un_sasync; /* noone is waiting for scmd */
int un_ltick; /* last time active */
struct mb_device *un_md; /* generic unit */
struct mb_ctlr *un_mc; /* generic controller */
} ipunits[NIP];
#define FUNNYBIT 4
#define FUNNY(u) ((u)&FUNNYBIT)
#define NORMAL(u) ((u)&~FUNNYBIT)
#define MAXHEAD 4 /* max heads to search for a label */
/*
* Data per controller
*/
struct ctlr {
struct unit *c_units[NUNIT];/* units on controller */
struct ipdevice *c_io; /* ptr to I/O space data */
caddr_t c_iopb; /* ptr to IOPB */
short c_type; /* controller type */
char c_present; /* controller is present */
/* current transfer: */
int c_baddr; /* physical buffer address */
daddr_t c_blkno; /* current block */
short c_nsect; /* block(sector) count */
short c_cmd; /* current command */
short c_retries; /* retry count */
short c_restores; /* restore count */
char c_wantint; /* expecting interrupt */
} ipctlrs[NIPC];
#define IP_R0(c) c->c_io->ip_r0
#define IP_R1(c) c->c_io->ip_r1
#define IP_R2(c) c->c_io->ip_r2
#define IP_R3(c) c->c_io->ip_r3
/*
* Error message control
*/
#define EL_RETRY 3
#define EL_REST 2
#define EL_FAIL 1
int iperrlvl = EL_RETRY;
#define IPERROR(err) iperrors[err-0x10]
struct error {
char e_retry; /* # of times to retry */
char e_restore; /* # of times to restore after retries */
char *e_name; /* error message */
} iperrors[] = {
0, 1, "disk not ready", /* 0x10 */
#define E_NOTREADY 0x10
0, 0, "invalid disk address", /* 0x11 */
1, 8, "seek error", /* 0x12 */
3, 1, "checksum error -- data field", /* 0x13 */
0, 0, "invalid command code", /* 0x14 */
#define E_INVCMD 0x14
0, 1, "invalid track in IOPB", /* 0x15 */
1, 1, "invalid sector in command", /* 0x16 */
1, 1, "?", /* 0x17 */
5, 1, "bus timeout", /* 0x18 */
3, 1, "write error", /* 0x19 */
0, 1, "disk write protected", /* 0x1a */
#define E_NOTSEL 0x1B
0, 3, "unit not selected", /* 0x1b */
3, 1, "no address mark -- header field", /* 0x1c */
3, 1, "no data mark -- data field", /* 0x1d */
#define E_UFAULT 0x1E
0, 3, "unit fault", /* 0x1e */
3, 1, "data overrun timeout", /* 0x1f */
0, 1, "surface overrun", /* 0x20 */
3, 1, "id field error -- wrong sector read", /* 0x21 */
3, 1, "id field or ECC error", /* 0x22 */
1, 1, "?", /* 0x23 */
1, 1, "?", /* 0x24 */
1, 1, "?", /* 0x25 */
3, 8, "no sector pulse", /* 0x26 */
3, 8, "data overrun", /* 0x27 */
3, 8, "no index pulse on write format", /* 0x28 */
3, 1, "sector not found", /* 0x29 */
3, 8, "id field error -- wrong head", /* 0x2a */
3, 8, "invalid sync in data field", /* 0x2b */
3, 8, "invalid sync in header field", /* 0x2c */
1, 8, "seek timeout error", /* 0x2d */
1, 8, "busy timeout", /* 0x2e */
1, 8, "not on-cylinder at beginning of a seek",/* 0x2f */
0, 3, "rtz timeout", /* 0x30 */
3, 8, "format overrun on data", /* 0x31 */
#define E_UNKNOWN 0x32
1, 1, "unknown error", /* 0x32 */
};
#define b_cylin b_resid
#ifdef INTRLVE
daddr_t dkblock();
#endif
int ipwstart, ipwatch(); /* Have started guardian */
int ipticks; /* timer for guardian */
#define IPTIMO 20 /* time until disk check */
/*
* Determine existence and type of controller
*/
ipprobe(reg, ctlr)
caddr_t reg;
{
register struct ctlr *c = &ipctlrs[ctlr];
register struct iopb0 *ip0;
register int nms, piopb;
c->c_io = (struct ipdevice *)reg;
if (pokec((char *)&IP_R0(c), (char)0))
return (0);
c->c_iopb = (caddr_t)rmalloc(iopbmap, (long)IPIOPBSZ);
if (c->c_iopb == NULL) {
printf("ipprobe: no iopb space\n");
return (0);
}
c->c_present = 1;
/* determine controller type */
clrint(c);
DELAY(1000);
bzero(c->c_iopb, IPIOPBSZ);
ip0 = (struct iopb0 *)c->c_iopb;
ip0->i0_cmd = IP_RESET;
ip0->i0_ioaddr = ((int)c->c_io - (int)mbio) & 0xFF;
piopb = (char *)c->c_iopb - DVMA; /* cntlr gets phys addr */
IP_R1(c) = piopb >> 16;
IP_R2(c) = (piopb >> 8) & 0xFF;
IP_R3(c) = piopb & 0xFF;
IP_R0(c) = IP_GO;
c->c_type = DKC_UNKNOWN;
for (nms = 0; nms < 100; nms++) {
DELAY(1000);
if (ip0->i0_status == IP_OK) {
c->c_type = DKC_SMD2180;
break;
}
}
clrint(c);
if (c->c_type == DKC_UNKNOWN) {
printf("ipc%d: Unknown controller type at mbio 0x%x\n",
ctlr, reg - mbio);
rmfree(iopbmap, (long)IPIOPBSZ, (long)c->c_iopb);
c->c_present = 0;
return (0);
}
return (sizeof (struct ipdevice));
}
/*
* See if a slave unit exists
* Since the Interphase controller won't
* tell us we always say yes.
*/
/*ARGSUSED*/
ipslave(md, reg)
struct mb_device *md;
caddr_t reg;
{
return (1);
}
ipattach(md)
register struct mb_device *md;
{
register struct ctlr *c = &ipctlrs[md->md_ctlr];
register struct unit *un = &ipunits[md->md_unit];
struct dk_label *l;
int head, i;
int unit = md->md_slave;
if (ipwstart == 0) {
timeout(ipwatch, (caddr_t)0, hz);
ipwstart++;
}
c->c_units[md->md_slave] = un;
un->un_md = md;
un->un_mc = md->md_mc;
/* Allocate space for label in Mainbus memory */
l = (struct dk_label *)rmalloc(iopbmap, (long)SECSIZE);
if (l == NULL) {
printf("ipattach: no space for disk label\n");
un->un_present = 0;
return;
}
/*
* Initialize unit (physical drive)
*/
un->un_g.dkg_ncyl = 1;
un->un_g.dkg_bcyl = 0;
un->un_g.dkg_nhead = MAXHEAD;
un->un_g.dkg_bhead = 0;
un->un_g.dkg_nsect = 2;
un->un_g.dkg_intrlv = 0;
un->un_g.dkg_gap1 = 20;
un->un_g.dkg_gap2 = 22;
if (simple(c, unit, IP_RESTORE) != 0) {
un->un_present = 0;
return;
}
/* Search for a label */
for (head = 0; head < MAXHEAD; head++) {
l->dkl_magic = 0; /* clear from previous try */
if (getlabel(c, unit, head, l) == 0)
continue;
if (islabel(un - ipunits, unit, head, l) == 0)
continue;
uselabel(un, l);
break;
}
rmfree(iopbmap, (long)SECSIZE, (long)l);
if (!un->un_present) {
for (i=0; i<NLPART; i++) {
un->un_map[i].dkl_cylno = 0;
un->un_map[i].dkl_nblk = 0;
}
}
}
static
uselabel(un, l)
register struct unit *un;
register struct dk_label *l;
{
int i, intrlv;
printf("ip%d: <%s>\n", un - ipunits, l->dkl_asciilabel);
un->un_present = 1;
un->un_g.dkg_ncyl = l->dkl_ncyl;
un->un_g.dkg_bcyl = 0; /* for now */
un->un_g.dkg_nhead = l->dkl_nhead;
un->un_g.dkg_bhead = l->dkl_bhead;
un->un_g.dkg_nsect = l->dkl_nsect;
un->un_g.dkg_gap1 = l->dkl_gap1;
un->un_g.dkg_gap2 = l->dkl_gap2;
un->un_g.dkg_intrlv = l->dkl_intrlv;
for (i = 0; i < NLPART; i++)
un->un_map[i] = l->dkl_map[i]; /* struct assignment */
if (un->un_md->md_dk >= 0) {
intrlv = un->un_g.dkg_intrlv;
if (intrlv <= 0 || intrlv >= un->un_g.dkg_nsect)
intrlv = 7;
dk_bps[un->un_md->md_dk] = SECSIZE*60*un->un_g.dkg_nsect/intrlv;
}
}
static
getlabel(c, unit, head, l)
register struct ctlr *c;
register struct dk_label *l;
{
int error, retries, restores;
register int ipn = (c->c_units[unit] - ipunits);
l->dkl_magic = 0;
clrint(c);
restores = 0;
do {
retries = 0;
do {
ipcmd(c, IP_READ, (char *)l-DVMA, unit, 0, head, 0, 1);
error = ipwait(c);
} while (error && retries++ < IPERROR(error).e_retry);
} while (error && restores++ < IPERROR(error).e_restore);
if (error) {
printf("ip%d: error %x reading label on head %d: %s\n",
ipn, error, head, IPERROR(error).e_name);
return (0);
}
return (1);
}
ipwait(c)
register struct ctlr *c;
{
register struct iopb0 *ip0;
while (IP_R0(c) & IP_BUSY)
DELAY(30);
ip0 = (struct iopb0 *)c->c_iopb;
while (ip0->i0_status==IP_DBUSY || ip0->i0_status==0)
DELAY(30);
clrint(c);
if (ip0->i0_status == IP_ERROR)
return (ip0->i0_error);
return (0);
}
static
islabel(ipn, unit, head, l)
register struct dk_label *l;
{
if (l->dkl_magic != DKL_MAGIC)
return (0);
if (!ck_cksum(l)) {
printf("ip%d: Corrupt label on head %d\n", ipn, head);
return (0);
}
if (head != l->dkl_bhead) {
printf("ip%d: Misplaced label on head %d\n", ipn, head);
return (0);
}
if (l->dkl_ppart != 0 && l->dkl_ppart != 1) {
printf("ip%d: Unsupported phys partition # %d\n",
ipn, l->dkl_ppart);
return (0);
}
if (FUNNY(unit) && !l->dkl_ppart)
return (0);
if (l->dkl_ppart && !FUNNY(unit))
return (0);
return (1);
}
/*
* Check the checksum of the label
*/
static
ck_cksum(l)
struct dk_label *l;
{
short *sp, sum = 0;
short count = sizeof(struct dk_label)/sizeof(short);
sp = (short *)l;
while (count--)
sum ^= *sp++;
return (sum ? 0 : 1);
}
/*
* Give a simple command to a controller
* and spin until done.
* Returns the error number or zero
*/
static
simple(c, unit, cmd)
struct ctlr *c;
{
clrint(c);
ipcmd(c, cmd, 0, unit, 0, 0, 0, 0);
return (ipwait(c));
}
/*
* Get rid of interrupt condition
* Sets clear bit and waits a while for it to take effect
*/
static
clrint(c)
struct ctlr *c;
{
while (IP_R0(c) & IP_BUSY)
DELAY(30);
IP_R0(c) = IP_CLRINT;
DELAY(30);
}
ipcmd(c, cmd, dmaddr, unit, cylinder, head, sector, secnt)
struct ctlr *c;
{
register int piopb;
register struct iopb0 *ip0;
unit = 1 << (unit&3);
piopb = (char *)c->c_iopb - DVMA; /* cntlr gets phys addr */
while (IP_R0(c) & IP_BUSY)
DELAY(30);
ip0 = (struct iopb0 *)c->c_iopb;
ip0->i0_cmd = cmd;
ip0->i0_status = 0;
ip0->i0_error = 0;
ip0->i0_unit_cylhi = (unit << 4) | ((cylinder>>8) & 0x0F);
ip0->i0_cylinder = cylinder & 0xFF;
ip0->i0_sector = sector;
ip0->i0_secnt = secnt;
ip0->i0_buf_xmb = ((dmaddr >> 16) & 0xF) | IP_BUS | IP_REL;
ip0->i0_buf_lsb = dmaddr & 0xFF;
ip0->i0_buf_msb = (dmaddr>>8) & 0xFF;
ip0->i0_head = head;
ip0->i0_ioaddr = ((int)c->c_io - (int)mbio) & 0xFF;
ip0->i0_burstlen = IP0_BURSTLEN;
ip0->i0_nxt_xmb = IP_BUS | IP_REL;
ip0->i0_nxt_lsb = 0;
ip0->i0_nxt_msb = 0;
ip0->i0_seg_lsb = 0;
ip0->i0_seg_msb = 0;
/* point controller at iopb and start it up */
IP_R1(c) = (piopb >> 16) | IP_BUS;
IP_R2(c) = (piopb >> 8) & 0xFF;
IP_R3(c) = piopb & 0xFF;
IP_R0(c) = IP_GO;
}
/*ARGSUSED*/
ipopen(dev, flag)
dev_t dev;
{
struct unit *un;
struct dk_label *l;
int i, head, unit;
unit = UNIT(dev);
un = &ipunits[unit];
if (un->un_mc == 0) /* never attached */
return (ENXIO);
if (!un->un_present) {
for (i = 0; i < NLPART; i++) {
un->un_map[i].dkl_cylno = 0;
un->un_map[i].dkl_nblk = 1;
}
un->un_g.dkg_ncyl = 1;
un->un_g.dkg_bcyl = 0;
un->un_g.dkg_nhead = MAXHEAD;
un->un_g.dkg_bhead = 0;
un->un_g.dkg_nsect = 2;
if (ipcommand(dev, IP_RESTORE, (daddr_t)0, (caddr_t)0, 0, 0)) {
return (EIO);
}
un->un_present = 1;
/* Allocate space for label in Multibus memory */
/* XXX wrong -- iopb addresses are not usable */
l = (struct dk_label *)rmalloc(iopbmap, (long)SECSIZE);
if (l == NULL) {
printf("ipopen: no buffer for disk label\n");
return (EIO);
}
/* Search for a label */
for (head = 0; head < MAXHEAD; head++) {
l->dkl_magic = 0; /* clear from previous try */
un->un_g.dkg_bhead = head;
if (ipcommand(dev, IP_READ, (daddr_t)0, (caddr_t)l,
SECSIZE, 0) != 0)
continue;
if (!islabel(un-ipunits, un->un_md->md_slave, head, l))
continue;
uselabel(un, l);
break;
}
if (head >= MAXHEAD)
un->un_g.dkg_bhead = 0;
rmfree(iopbmap, (long)SECSIZE, (long)l);
}
return (0);
}
ipsize(dev)
dev_t dev;
{
struct unit *un = &ipunits[UNIT(dev)];
struct dk_map *lp = &un->un_map[LPART(dev)];
if (!un->un_present)
return (-1);
return (lp->dkl_nblk);
}
ipstrategy(bp)
register struct buf *bp;
{
register struct unit *un;
register struct dk_map *lp;
register struct buf *dp;
register int unit, s;
daddr_t bn;
unit = dkunit(bp);
if (unit >= NIP)
goto bad;
un = &ipunits[unit];
lp = &un->un_map[LPART(bp->b_dev)];
if (!un->un_present && bp != &un->un_sbuf)
goto bad;
bn = dkblock(bp);
if (bn > lp->dkl_nblk || lp->dkl_nblk == 0)
goto bad;
if (bn == lp->dkl_nblk) { /* EOF */
bp->b_resid = bp->b_bcount;
iodone(bp);
return;
}
bp->b_cylin = bn / (un->un_g.dkg_nsect * un->un_g.dkg_nhead);
bp->b_cylin += lp->dkl_cylno + un->un_g.dkg_bcyl;
dp = &un->un_utab;
s = splx(pritospl(un->un_mc->mc_intpri));
disksort((struct diskhd *)dp, bp);
if (dp->b_active == 0) {
(void) ipustart(un);
bp = &un->un_mc->mc_tab;
if (bp->b_actf && bp->b_active == 0)
(void) ipstart(un->un_mc);
}
(void) splx(s);
return;
bad:
bp->b_flags |= B_ERROR;
iodone(bp);
return;
}
ipcommand(dev, cmd, daddr, b_addr, blen, nowait)
dev_t dev;
daddr_t daddr;
caddr_t b_addr;
{
register struct unit *un = &ipunits[UNIT(dev)];
register struct buf *bp;
int s;
bp = &un->un_sbuf;
s = splx(pritospl(un->un_mc->mc_intpri));
while (bp->b_flags&B_BUSY) {
if (nowait) {
(void) splx(s);
return (0);
}
bp->b_flags |= B_WANTED;
(void) sleep((caddr_t)bp, PRIBIO);
}
bp->b_flags = B_BUSY|B_READ;
(void) splx(s);
bp->b_dev = dev;
un->un_scmd = cmd;
un->un_sasync = nowait;
bp->b_bcount = blen;
bp->b_un.b_addr = b_addr;
bp->b_blkno = daddr;
ipstrategy(bp);
if (nowait)
return (0);
iowait(bp);
bp->b_flags &= ~B_BUSY;
if (bp->b_flags&B_WANTED)
wakeup((caddr_t)bp);
return (bp->b_flags & B_ERROR);
}
/*
* Unit start routine.
*/
ipustart(un)
register struct unit *un;
{
register struct buf *dp;
register struct mb_device *md;
register struct mb_ctlr *mc;
if (un == 0)
return;
md = un->un_md;
mc = un->un_mc;
dk_busy &= ~(1<<md->md_dk);
dp = &un->un_utab;
if (dp->b_actf == NULL)
return;
if (!dp->b_active) {
dp->b_active = 1;
/*
* Mark unit busy for iostat.
*/
if (md->md_dk >= 0) {
dk_busy |= 1<<md->md_dk;
dk_seek[md->md_dk]++;
}
}
/*
* Device is ready to go.
* Put it on the ready queue for the controller
* (unless its already there.)
*/
if (dp->b_active != 2) {
dp->b_forw = NULL;
if (mc->mc_tab.b_actf == NULL)
mc->mc_tab.b_actf = dp;
else
mc->mc_tab.b_actl->b_forw = dp;
mc->mc_tab.b_actl = dp;
dp->b_active = 2;
}
}
/*
* Set up a transfer for the controller
*/
ipstart(mc)
register struct mb_ctlr *mc;
{
register struct buf *bp, *dp;
register struct unit *un;
register struct ctlr *c;
register struct dk_map *lp;
daddr_t bn;
loop:
/*
* Pull a request off the controller queue
*/
if ((dp = mc->mc_tab.b_actf) == NULL)
return;
if ((bp = dp->b_actf) == NULL) {
mc->mc_tab.b_actf = dp->b_forw;
goto loop;
}
/*
* Mark controller busy, and
* determine destination of this request.
*/
mc->mc_tab.b_active++;
un = &ipunits[dkunit(bp)];
c = &ipctlrs[mc->mc_ctlr];
bn = dkblock(bp);
lp = &un->un_map[LPART(bp->b_dev)];
c->c_nsect = howmany(bp->b_bcount, SECSIZE);
c->c_nsect = min((u_int)c->c_nsect, (u_int)(lp->dkl_nblk-bp->b_blkno));
bn += (lp->dkl_cylno * un->un_g.dkg_nhead * un->un_g.dkg_nsect);
c->c_blkno = bn;
bp->b_resid = bp->b_bcount;
if (bp == &un->un_sbuf &&
un->un_scmd != IP_READ && un->un_scmd != IP_WRITE)
ipgo(mc);
else
(void) mbgo(mc);
}
/*
* Start up a transfer.
* Called via mbgo after buffer is
* mapped into Mainbus memory space.
*/
ipgo(mc)
register struct mb_ctlr *mc;
{
register struct unit *un;
register struct ctlr *c;
register struct buf *bp, *dp;
int unit;
c = &ipctlrs[mc->mc_ctlr];
dp = mc->mc_tab.b_actf;
if (dp == NULL || dp->b_actf == NULL)
panic("ipgo queueing error 1");
bp = dp->b_actf;
unit = UNIT(bp->b_dev);
un = &ipunits[unit];
if (dp != &un->un_utab)
panic("ipgo queueing error 2");
if (bp == &un->un_sbuf) {
c->c_nsect = howmany(bp->b_bcount, SECSIZE);
c->c_cmd = un->un_scmd;
if (c->c_cmd == IP_READ || c->c_cmd == IP_WRITE)
c->c_baddr = MBI_ADDR(mc->mc_mbinfo);
else
c->c_baddr = (int)bp->b_un.b_addr; /* not an address */
} else {
if (bp->b_flags & B_READ)
c->c_cmd = IP_READ;
else
c->c_cmd = IP_WRITE;
c->c_baddr = MBI_ADDR(mc->mc_mbinfo);
}
if ((unit = un->un_md->md_dk) >= 0) {
dk_busy |= 1<<unit;
dk_xfer[unit]++;
dk_wds[unit] += bp->b_bcount>>6;
}
ipchunk(c, un);
}
ipchunk(c, un)
register struct ctlr *c;
register struct unit *un;
{
int cyl, head, sect, nsect;
sect = c->c_blkno % un->un_g.dkg_nsect;
head = c->c_blkno / un->un_g.dkg_nsect;
cyl = head / un->un_g.dkg_nhead;
head = head % un->un_g.dkg_nhead;
head += un->un_g.dkg_bhead;
cyl += un->un_g.dkg_bcyl;
nsect = MIN(c->c_nsect, un->un_g.dkg_nsect - sect);
c->c_wantint = 1;
ipcmd(c, c->c_cmd, c->c_baddr, un->un_md->md_slave,
cyl, head, sect, nsect);
}
/*
* Handle a disk interrupt.
*/
ipint(ctlr)
int ctlr;
{
register struct ctlr *c = &ipctlrs[ctlr];
register struct unit *un;
register struct buf *bp;
register struct iopb0 *ip0;
register struct mb_ctlr *mc;
register struct mb_device *md;
struct error *e;
register int status, cmd, error, secnt, unit;
if (!c->c_wantint) {
clrint(c);
return;
}
c->c_wantint = 0;
while (IP_R0(c) & IP_BUSY)
;
ip0 = (struct iopb0 *)c->c_iopb;
while (ip0->i0_status == IP_DBUSY)
;
IP_R0(c) = IP_CLRINT;
status = ip0->i0_status;
cmd = ip0->i0_cmd;
secnt = ip0->i0_secnt;
error = ip0->i0_error;
ip0->i0_status = 0;
/*
* Get device and block structures, and a pointer
* to the mb_device for the drive.
*/
mc = ipcinfo[ctlr];
bp = mc->mc_tab.b_actf->b_actf;
md = ipdinfo[dkunit(bp)];
un = &ipunits[dkunit(bp)];
un->un_ltick = ipticks;
dk_busy &= ~(1 << md->md_dk);
if (bp == NULL) {
printf("ipint: bad bp\n");
return;
}
if (status == IP_ERROR) { /* error handling! */
e = &IPERROR(error);
if (c->c_retries++ >= e->e_retry) {
/* retries exhausted, try restore */
c->c_retries = 0;
if (c->c_restores++ >= e->e_restore) {
/* complete failure */
if (iperrlvl >= EL_FAIL)
errmsg(cmd, bp, e, "failed");
bp->b_flags |= B_ERROR;
if (bp == &un->un_sbuf &&
un->un_scmd == IP_RESTORE) {
un->un_present = 0;
if (un->un_md->md_dk >= 0)
dk_bps[un->un_md->md_dk] = 0;
printf("ip%d: offline\n", dkunit(bp));
}
if (bp == &un->un_sbuf &&
un->un_scmd != IP_READ &&
un->un_scmd != IP_WRITE)
ipdone(mc);
else
mbdone(mc);
} else {
/* do a restore */
if (iperrlvl >= EL_REST)
errmsg(cmd, bp, e, "restore");
unit = UNIT(bp->b_dev);
unit = ipunits[unit].un_md->md_slave;
c->c_wantint = 1;
ipcmd(c, IP_RESTORE, 0, unit,0,0,0,0);
}
} else {
/* retry */
if (iperrlvl >= EL_RETRY)
errmsg(cmd, bp, e, "retry");
ipchunk(c, un);
}
return;
}
/* status == OK */
if (cmd == IP_RESTORE &&
!(bp == &un->un_sbuf && un->un_scmd == IP_RESTORE)) {
/* error recovery */
ipchunk(c, un);
return;
}
/* transfer worked */
if (cmd == IP_READ || cmd == IP_WRITE)
c->c_nsect -= secnt;
else
c->c_nsect = 0;
c->c_blkno += secnt;
c->c_baddr += (secnt * SECSIZE);
bp->b_resid -= (secnt * SECSIZE);
if (bp->b_resid < 0)
bp->b_resid = 0;
if (c->c_nsect != 0) /* more to do */
ipchunk(c, un);
else {
c->c_retries = c->c_restores = 0;
if (bp == &un->un_sbuf && un->un_scmd != IP_READ &&
un->un_scmd != IP_WRITE)
ipdone(mc);
else
mbdone(mc);
}
}
/*
* Handle a polling disk interrupt.
*/
ippoll()
{
register struct ctlr *c;
register int ctlr, serviced = 0;
for (ctlr = 0, c = ipctlrs; ctlr < NIPC; ctlr++, c++) {
if (!c->c_present || (IP_R0(c) & IP_COMPLETE) == 0)
continue;
serviced = 1;
ipint(ctlr);
}
return (serviced);
}
/*
* Clean up queues, free resources, and start next I/O
* all done after I/O finishes. Called by mbdone.
*/
ipdone(mc)
register struct mb_ctlr *mc;
{
register struct buf *bp, *dp;
register struct unit *un;
bp = mc->mc_tab.b_actf->b_actf;
un = &ipunits[UNIT(bp->b_dev)];
/* advance controller queue */
dp = mc->mc_tab.b_actf;
mc->mc_tab.b_active = 0;
mc->mc_tab.b_actf = dp->b_forw;
/* advance unit queue */
dp->b_active = 0;
dp->b_actf = bp->av_forw;
iodone(bp);
if (bp == &un->un_sbuf && un->un_sasync)
bp->b_flags &= ~B_BUSY;
/* start next I/O on unit */
if (dp->b_actf)
(void) ipustart(un);
/* start next I/O on controller */
if (mc->mc_tab.b_actf && mc->mc_tab.b_active == 0)
(void) ipstart(mc);
}
#define CMDNAME(n) ipcmdnames[n-IP_READ]
char *ipcmdnames[] = {
"read",
"write",
"verify",
"format",
"map",
"switch",
"init",
"<bad cmd>",
"restore",
"seek",
"rtz",
"spindown",
"<bad cmd>",
"<bad cmd>",
"reset",
};
static
errmsg(cmd, bp, e, action)
struct buf *bp;
struct error *e;
char *action;
{
printf("ip%d%c: %s %s (%s) blk %d\n", UNIT(bp->b_dev),
LPART(bp->b_dev)+'a', CMDNAME(cmd), action, e->e_name, bp->b_blkno);
}
ipread(dev, uio)
dev_t dev;
struct uio *uio;
{
register int unit = UNIT(dev);
if (unit >= NIP)
return (ENXIO);
return (physio(ipstrategy, &ipunits[unit].un_rtab, dev, B_READ,
minphys, uio));
}
ipwrite(dev, uio)
dev_t dev;
struct uio *uio;
{
register int unit = UNIT(dev);
if (unit >= NIP)
return (ENXIO);
return (physio(ipstrategy, &ipunits[unit].un_rtab, dev, B_WRITE,
minphys, uio));
}
/*ARGSUSED*/
ipioctl(dev, cmd, data, flag)
dev_t dev;
caddr_t data;
{
register struct unit *un = &ipunits[UNIT(dev)];
register struct dk_map *lp = &un->un_map[LPART(dev)];
struct dk_info *inf;
switch (cmd) {
case DKIOCINFO:
inf = (struct dk_info *)data;
inf->dki_ctlr = getdevaddr(un->un_mc->mc_addr);
inf->dki_unit = un->un_md->md_slave;
inf->dki_ctype = ipctlrs[un->un_mc->mc_ctlr].c_type;
inf->dki_flags = DKI_FMTTRK+DKI_MAPTRK;
break;
case DKIOCGGEOM:
*(struct dk_geom *)data = un->un_g; /* struct assignment */
break;
case DKIOCSGEOM:
if (!suser())
return (u.u_error);
un->un_g = *(struct dk_geom *)data; /* struct assignment */
break;
case DKIOCGPART:
*(struct dk_map *)data = *lp; /* struct assignment */
break;
case DKIOCSPART:
if (!suser())
return (u.u_error);
*lp = *(struct dk_map *)data; /* struct assignment */
break;
default:
return (ENOTTY);
}
return (0);
}
/*
* Wake up every so often and poke the disk to make
* sure it hasn't disappeared
*
* SOMEDAY WE SHOULD HANDLE LOST INTERRUPTS HERE (but no problems yet)
*/
ipwatch()
{
register struct unit *un;
ipticks++;
for (un = &ipunits[0]; un < &ipunits[NIP]; un++) {
if (!un->un_present)
continue;
if (ipticks - un->un_ltick < IPTIMO)
continue;
(void) ipcommand((un-ipunits)*NUNIT, IP_RESTORE,
(daddr_t)0, (caddr_t)0, 0, 1);
}
timeout(ipwatch, (caddr_t)0, hz);
}
ipdump(dev, addr, blkno, nblk)
dev_t dev;
caddr_t addr;
daddr_t blkno, nblk;
{
register struct unit *un = &ipunits[UNIT(dev)];
register struct dk_map *lp = &un->un_map[LPART(dev)];
register struct ctlr *c = &ipctlrs[un->un_mc->mc_ctlr];
int unit = un->un_md->md_slave;
int nsect, sect, head, cyl;
int err;
if (!un->un_present)
return (ENXIO);
if (blkno >= lp->dkl_nblk || (blkno+nblk) > lp->dkl_nblk)
return (EINVAL);
do {
nsect = un->un_g.dkg_nsect - (blkno % un->un_g.dkg_nsect);
nsect = min((u_int)nsect, (u_int)nblk);
nblk -= nsect;
sect = blkno % un->un_g.dkg_nsect;
head = blkno / un->un_g.dkg_nsect;
cyl = head / un->un_g.dkg_nhead;
head = head % un->un_g.dkg_nhead;
head += un->un_g.dkg_bhead;
cyl += un->un_g.dkg_bcyl;
cyl += lp->dkl_cylno;
ipcmd(c, IP_WRITE, (char *)addr - DVMA, unit,
cyl, head, sect, nsect);
err = ipwait(c);
blkno += nsect;
addr += nsect*DEV_BSIZE;
} while (nblk > 0 && !err);
return (err ? EIO : 0);
}