Beispiel #1
0
void
wskbd_input(struct device *dev, u_int type, int value)
{
	struct wskbd_softc *sc = (struct wskbd_softc *)dev; 
#if NWSDISPLAY > 0
	int num;
#endif

#if NWSDISPLAY > 0
	if (sc->sc_repeating) {
		sc->sc_repeating = 0;
		timeout_del(&sc->sc_repeat_ch);
	}

	/*
	 * If /dev/wskbdN is not connected in event mode translate and
	 * send upstream.
	 */
	if (sc->sc_translating) {
#ifdef HAVE_BURNER_SUPPORT
		if (type == WSCONS_EVENT_KEY_DOWN && sc->sc_displaydv != NULL)
			wsdisplay_burn(sc->sc_displaydv, WSDISPLAY_BURN_KBD);
#endif
		num = wskbd_translate(sc->id, type, value);
		if (num > 0) {
			if (sc->sc_displaydv != NULL) {
#ifdef HAVE_SCROLLBACK_SUPPORT
				/* XXX - Shift_R+PGUP(release) emits PrtSc */
				if (sc->id->t_symbols[0] != KS_Print_Screen) {
					wsscrollback(sc->sc_displaydv,
					    WSDISPLAY_SCROLL_RESET);
				}
#endif
				wsdisplay_kbdinput(sc->sc_displaydv,
				    sc->id->t_keymap.layout,
				    sc->id->t_symbols, num);
			}

			if (sc->sc_keyrepeat_data.del1 != 0) {
				sc->sc_repeating = num;
				timeout_add_msec(&sc->sc_repeat_ch,
				    sc->sc_keyrepeat_data.del1);
			}
		}
		return;
	}
#endif

	wskbd_deliver_event(sc, type, value);

#if NWSDISPLAY > 0
	/* Repeat key presses if enabled. */
	if (type == WSCONS_EVENT_KEY_DOWN && sc->sc_keyrepeat_data.del1 != 0) {
		sc->sc_repeat_type = type;
		sc->sc_repeat_value = value;
		sc->sc_repeating = 1;
		timeout_add_msec(&sc->sc_repeat_ch, sc->sc_keyrepeat_data.del1);
	}
#endif
}
Beispiel #2
0
void
panel_repeat(void *v)
{
	struct panel_softc *sc = (struct panel_softc *)v;
	uint8_t reg;

	reg = bus_space_read_1(sc->sc_iot, sc->sc_ioh, 0);

#if NAUDIO > 0 && NWSKBD > 0
	/*
	 * Volume button autorepeat.
	 */
	if (sys_config.system_subtype == IP22_INDY)
		panel_volume_adjust(sc, reg);
#endif

	if (hpc_is_intr_pending(sc->sc_irq)) {
		/*
		 * Keep acking everything to get the interrupt finally
		 * unlatched.
		 */
		bus_space_write_1(sc->sc_iot, sc->sc_ioh, 0,
		    IOC_PANEL_POWER_STATE | IOC_PANEL_POWER_IRQ |
		    IOC_PANEL_VDOWN_IRQ | IOC_PANEL_VDOWN_HOLD |
		    IOC_PANEL_VUP_IRQ | IOC_PANEL_VUP_HOLD);
		bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, 1,
		    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);

		timeout_add_msec(&sc->sc_repeat_tmo, PANEL_REPEAT_NEXT);
	} else {
		hpc_intr_enable(sc->sc_ih);
	}
}
Beispiel #3
0
/*
 * octeon_eth_tick_free
 *
 * => garbage collect send gather buffer / mbuf
 * => called at softclock
 */
void
octeon_eth_tick_free(void *arg)
{
	struct octeon_eth_softc *sc = arg;
	int timo;
	int s;

	s = splnet();
	/* XXX */
	if (sc->sc_soft_req_cnt > 0) {
		octeon_eth_send_queue_flush_prefetch(sc);
		octeon_eth_send_queue_flush_fetch(sc);
		octeon_eth_send_queue_flush(sc);
		octeon_eth_send_queue_flush_sync(sc);
	}
	/* XXX */

	/* XXX ??? */
	timo = hz - (100 * sc->sc_ext_callback_cnt);
	if (timo < 10)
		 timo = 10;
	timeout_add_msec(&sc->sc_tick_free_ch, 1000 * timo / hz);
	/* XXX */
	splx(s);
}
Beispiel #4
0
void
wskbd_repeat(void *v)
{
	struct wskbd_softc *sc = (struct wskbd_softc *)v;
	int s = spltty();

	if (sc->sc_repeating == 0) {
		/*
		 * race condition: a "key up" event came in when wskbd_repeat()
		 * was already called but not yet spltty()'d
		 */
		splx(s);
		return;
	}
	if (sc->sc_translating) {
		/* deliver keys */
		if (sc->sc_displaydv != NULL)
			wsdisplay_kbdinput(sc->sc_displaydv,
			    sc->id->t_keymap.layout,
			    sc->id->t_symbols, sc->sc_repeating);
	} else {
		/* queue event */
		wskbd_deliver_event(sc, sc->sc_repeat_type,
		    sc->sc_repeat_value);
	}
	if (sc->sc_keyrepeat_data.delN != 0)
		timeout_add_msec(&sc->sc_repeat_ch, sc->sc_keyrepeat_data.delN);
	splx(s);
}
Beispiel #5
0
void
rdrand(void *v)
{
	struct timeout *tmo = v;
	extern int      has_rdrand;
	extern int      has_rdseed;
	uint32_t r;
	uint8_t valid;
	int i;

	if (has_rdrand == 0 && has_rdseed == 0)
		return;
	for (i = 0; i < 4; i++) {
		if (has_rdseed)
			__asm volatile(
			    "rdseed	%0\n\t"
			    "setc	%1\n"
			    : "=r" (r), "=qm" (valid) );
		if (has_rdseed == 0 || valid == 0)
			__asm volatile(
			    "rdrand	%0\n\t"
			    "setc	%1\n"
			    : "=r" (r), "=qm" (valid) );
		if (valid)
			add_true_randomness(r);
	}

	if (tmo)
		timeout_add_msec(tmo, 10);
}
void
ykbec_bell(void *arg, u_int pitch, u_int period, u_int volume, int poll)
{
	struct ykbec_softc *sc = (struct ykbec_softc *)arg;
	int bctrl;
	int s;

	s = spltty();
	bctrl = ykbec_read(sc, REG_BEEP_CONTROL);
	if (volume == 0 || timeout_pending(&sc->sc_bell_tmo)) {
		timeout_del(&sc->sc_bell_tmo);
		/* inline ykbec_bell_stop(arg); */
		ykbec_write(sc, REG_BEEP_CONTROL, bctrl & ~BEEP_ENABLE);
	}

	if (volume != 0) {
		ykbec_write(sc, REG_BEEP_CONTROL, bctrl | BEEP_ENABLE);
		if (poll) {
			delay(period * 1000);
			ykbec_write(sc, REG_BEEP_CONTROL, bctrl & ~BEEP_ENABLE);
		} else {
			timeout_add_msec(&sc->sc_bell_tmo, period);
		}
	}
	splx(s);
}
Beispiel #7
0
/**
 * drm_vblank_put - give up ownership of vblank events
 * @dev: DRM device
 * @crtc: which counter to give up
 *
 * Release ownership of a given vblank counter, turning off interrupts
 * if possible. Disable interrupts after drm_vblank_offdelay milliseconds.
 */
void drm_vblank_put(struct drm_device *dev, int crtc)
{
	BUG_ON(atomic_read(&dev->vblank_refcount[crtc]) == 0);

	/* Last user schedules interrupt disable */
	if (atomic_dec_and_test(&dev->vblank_refcount[crtc]) &&
	    (drm_vblank_offdelay > 0))
		timeout_add_msec(&dev->vblank_disable_timer, drm_vblank_offdelay);
}
Beispiel #8
0
int
panel_intr(void *v)
{
	struct panel_softc *sc = (struct panel_softc *)v;
	uint8_t reg, ack;
	extern int allowpowerdown;

	reg = bus_space_read_1(sc->sc_iot, sc->sc_ioh, 0);

	ack = IOC_PANEL_POWER_STATE;
	if (sys_config.system_subtype == IP22_INDIGO2 ||
	    (reg & IOC_PANEL_POWER_IRQ) == 0)
		ack |= IOC_PANEL_POWER_IRQ;
	if (sys_config.system_subtype == IP22_INDY) {
		if ((reg & IOC_PANEL_VDOWN_IRQ) == 0)
			ack |= IOC_PANEL_VDOWN_IRQ;
		if ((reg & IOC_PANEL_VUP_IRQ) == 0)
			ack |= IOC_PANEL_VUP_IRQ;
	}
	bus_space_write_1(sc->sc_iot, sc->sc_ioh, 0, ack);
	bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, 1,
	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);

	/*
	 * Panel interrupts are latched for about 300 msec after being
	 * acked, and not until all buttons are depressed.
	 * We need to temporary disable the interrupt and switch to polling,
	 * until the interrupt can be enabled again.
	 */
	hpc_intr_disable(sc->sc_ih);

	/*
	 * If the power button is down, try and issue a shutdown immediately
	 * (if allowed).
	 */
	if (sys_config.system_subtype == IP22_INDIGO2 ||
	    (reg & IOC_PANEL_POWER_IRQ) == 0) {
		if (allowpowerdown == 1) {
			allowpowerdown = 0;
			prsignal(initprocess, SIGUSR2);
		}
	}

#if NAUDIO > 0 && NWSKBD > 0
	/*
	 * If any of the volume buttons is down, update volume.
	 */
	if (sys_config.system_subtype == IP22_INDY)
		panel_volume_adjust(sc, reg);
#endif

	timeout_add_msec(&sc->sc_repeat_tmo, PANEL_REPEAT_FIRST);

	return 1;
}
Beispiel #9
0
void
sunkbd_input(struct sunkbd_softc *sc, u_int8_t *buf, u_int buflen)
{
	u_int type;
	int value;
	int s;

	if (sc->sc_wskbddev == NULL)
		return;	/* why bother */

#ifdef WSDISPLAY_COMPAT_RAWKBD
	if (sc->sc_rawkbd) {
		u_char rbuf[SUNKBD_MAX_INPUT_SIZE * 2];
		int c, rlen, npress;

		timeout_del(&sc->sc_rawrepeat_tmo);

		npress = rlen = 0;
		while (buflen-- != 0) {
			(*sc->sc_decode)(*buf++, &type, &value);
			c = sunkbd_rawmap[value];
			if (c == RAWKEY_Null)
				continue;
			/* fake extended scancode if necessary */
			if (c & 0x80)
				rbuf[rlen++] = 0xe0;
			rbuf[rlen] = c & 0x7f;
			if (type == WSCONS_EVENT_KEY_UP)
				rbuf[rlen] |= 0x80;
			else {
				/* remember down keys for autorepeat */
				if (c & 0x80)
					sc->sc_rep[npress++] = 0xe0;
				sc->sc_rep[npress++] = c & 0x7f;
			}
			rlen++;
		}

		s = spltty();
		wskbd_rawinput(sc->sc_wskbddev, rbuf, rlen);
		splx(s);
		sc->sc_nrep = npress;
		if (npress != 0)
			timeout_add_msec(&sc->sc_rawrepeat_tmo, REP_DELAY1);
	} else
#endif
	{
		s = spltty();
		while (buflen-- != 0) {
			(*sc->sc_decode)(*buf++, &type, &value);
			wskbd_input(sc->sc_wskbddev, type, value);
		}
		splx(s);
	}
}
Beispiel #10
0
void
sunkbd_rawrepeat(void *v)
{
	struct sunkbd_softc *sc = v;
	int s;

	s = spltty();
	wskbd_rawinput(sc->sc_wskbddev, sc->sc_rep, sc->sc_nrep);
	splx(s);
	timeout_add_msec(&sc->sc_rawrepeat_tmo, REP_DELAYN);
}
void
pffasttimo(void *arg)
{
	struct timeout *to = (struct timeout *)arg;
	struct domain *dp;
	struct protosw *pr;

	for (dp = domains; dp; dp = dp->dom_next)
		for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
			if (pr->pr_fasttimo)
				(*pr->pr_fasttimo)();
	timeout_add_msec(to, 200);
}
Beispiel #12
0
void
octrng_rnd(void *arg)
{
	struct octrng_softc *sc = arg;
	uint64_t value;

	value = bus_space_read_8(sc->sc_iot, sc->sc_ioh, OCTRNG_ENTROPY_REG);

	DPRINTF(("%#llX ", value));	/* WARNING: very verbose */

	add_true_randomness(value);
	timeout_add_msec(&sc->sc_to, 10);
}
/*
 * Send an EAPOL-Key frame to node `ni'.  If MIC or encryption is required,
 * the PTK must be passed (otherwise it can be set to NULL.)
 */
int
ieee80211_send_eapol_key(struct ieee80211com *ic, struct mbuf *m,
    struct ieee80211_node *ni, const struct ieee80211_ptk *ptk)
{
	struct ifnet *ifp = &ic->ic_if;
	struct ether_header *eh;
	struct ieee80211_eapol_key *key;
	u_int16_t info;
	int s, len, error;

	M_PREPEND(m, sizeof(struct ether_header), M_DONTWAIT);
	if (m == NULL)
		return ENOMEM;
	/* no need to m_pullup here (ok by construction) */
	eh = mtod(m, struct ether_header *);
	eh->ether_type = htons(ETHERTYPE_PAE);
	IEEE80211_ADDR_COPY(eh->ether_shost, ic->ic_myaddr);
	IEEE80211_ADDR_COPY(eh->ether_dhost, ni->ni_macaddr);

	key = (struct ieee80211_eapol_key *)&eh[1];
	key->version = EAPOL_VERSION;
	key->type = EAPOL_KEY;
	key->desc = (ni->ni_rsnprotos == IEEE80211_PROTO_RSN) ?
	    EAPOL_KEY_DESC_IEEE80211 : EAPOL_KEY_DESC_WPA;

	info = BE_READ_2(key->info);
	/* use V3 descriptor if KDF is SHA256-based */
	if (ieee80211_is_sha256_akm(ni->ni_rsnakms))
		info |= EAPOL_KEY_DESC_V3;
	/* use V2 descriptor if pairwise or group cipher is CCMP */
	else if (ni->ni_rsncipher == IEEE80211_CIPHER_CCMP ||
	    ni->ni_rsngroupcipher == IEEE80211_CIPHER_CCMP)
		info |= EAPOL_KEY_DESC_V2;
	else
		info |= EAPOL_KEY_DESC_V1;
	BE_WRITE_2(key->info, info);

	len = m->m_len - sizeof(struct ether_header);
	BE_WRITE_2(key->paylen, len - sizeof(*key));
	BE_WRITE_2(key->len, len - 4);

#ifndef IEEE80211_STA_ONLY
	if (info & EAPOL_KEY_ENCRYPTED) {
		if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) {
			/* clear "Encrypted" bit for WPA */
			info &= ~EAPOL_KEY_ENCRYPTED;
			BE_WRITE_2(key->info, info);
		}
		ieee80211_eapol_key_encrypt(ic, key, ptk->kek);

		if ((info & EAPOL_KEY_VERSION_MASK) != EAPOL_KEY_DESC_V1) {
			/* AES Key Wrap adds 8 bytes + padding */
			m->m_pkthdr.len = m->m_len =
			    sizeof(*eh) + 4 + BE_READ_2(key->len);
		}
	}
#endif
	if (info & EAPOL_KEY_KEYMIC)
		ieee80211_eapol_key_mic(key, ptk->kck);

	len = m->m_pkthdr.len;
	s = splnet();
#ifndef IEEE80211_STA_ONLY
	/* start a 100ms timeout if an answer is expected from supplicant */
	if (info & EAPOL_KEY_KEYACK)
		timeout_add_msec(&ni->ni_eapol_to, 100);
#endif
	IFQ_ENQUEUE(&ifp->if_snd, m, NULL, error);
	if (error == 0) {
		ifp->if_obytes += len;
		if ((ifp->if_flags & IFF_OACTIVE) == 0)
			(*ifp->if_start)(ifp);
	}
	splx(s);

	return error;
}
Beispiel #14
0
int
vnet_rx_intr(void *arg)
{
	struct vnet_softc *sc = arg;
	struct ldc_conn *lc = &sc->sc_lc;
	uint64_t rx_head, rx_tail, rx_state;
	struct ldc_pkt *lp;
	int err;

	err = hv_ldc_rx_get_state(lc->lc_id, &rx_head, &rx_tail, &rx_state);
	if (err == H_EINVAL)
		return (0);
	if (err != H_EOK) {
		printf("hv_ldc_rx_get_state %d\n", err);
		return (0);
	}

	if (rx_state != lc->lc_rx_state) {
		switch (rx_state) {
		case LDC_CHANNEL_DOWN:
			DPRINTF(("Rx link down\n"));
			lc->lc_tx_seqid = 0;
			lc->lc_state = 0;
			lc->lc_reset(lc);
			break;
		case LDC_CHANNEL_UP:
			DPRINTF(("Rx link up\n"));
			timeout_add_msec(&sc->sc_handshake_to, 500);
			break;
		case LDC_CHANNEL_RESET:
			DPRINTF(("Rx link reset\n"));
			lc->lc_tx_seqid = 0;
			lc->lc_state = 0;
			lc->lc_reset(lc);
			timeout_add_msec(&sc->sc_handshake_to, 500);
			break;
		}
		lc->lc_rx_state = rx_state;
		return (1);
	}

	if (rx_head == rx_tail)
		return (0);

	lp = (struct ldc_pkt *)(lc->lc_rxq->lq_va + rx_head);
	switch (lp->type) {
	case LDC_CTRL:
		ldc_rx_ctrl(lc, lp);
		break;

	case LDC_DATA:
		ldc_rx_data(lc, lp);
		break;

	default:
		DPRINTF(("%0x02/%0x02/%0x02\n", lp->type, lp->stype,
		    lp->ctrl));
		ldc_reset(lc);
		break;
	}

	if (lc->lc_state == 0)
		return (1);

	rx_head += sizeof(*lp);
	rx_head &= ((lc->lc_rxq->lq_nentries * sizeof(*lp)) - 1);
	err = hv_ldc_rx_set_qhead(lc->lc_id, rx_head);
	if (err != H_EOK)
		printf("%s: hv_ldc_rx_set_qhead %d\n", __func__, err);

	return (1);
}
void
ahd_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nsegments)
{
    struct	scb *scb;
    struct	scsi_xfer *xs;
    struct	ahd_softc *ahd;
    struct	ahd_initiator_tinfo *tinfo;
    struct	ahd_tmode_tstate *tstate;
    u_int	mask;
    int	s;

    scb = (struct scb *)arg;
    xs = scb->xs;
    xs->error = CAM_REQ_INPROG;
    xs->status = 0;
    ahd = (struct ahd_softc *)xs->sc_link->adapter_softc;

    if (nsegments != 0) {
        void *sg;
        int op;
        u_int i;

        ahd_setup_data_scb(ahd, scb);

        /* Copy the segments into our SG list */
        for (i = nsegments, sg = scb->sg_list; i > 0; i--) {

            sg = ahd_sg_setup(ahd, scb, sg, dm_segs->ds_addr,
                              dm_segs->ds_len,
                              /*last*/i == 1);
            dm_segs++;
        }

        if ((xs->flags & SCSI_DATA_IN) != 0)
            op = BUS_DMASYNC_PREREAD;
        else
            op = BUS_DMASYNC_PREWRITE;

        bus_dmamap_sync(ahd->parent_dmat, scb->dmamap, 0,
                        scb->dmamap->dm_mapsize, op);

    }

    ahd_lock(ahd, &s);

    /*
     * Last time we need to check if this SCB needs to
     * be aborted.
     */
    if (xs->flags & ITSDONE) {
        if (nsegments != 0)
            bus_dmamap_unload(ahd->parent_dmat,
                              scb->dmamap);
        ahd_free_scb(ahd, scb);
        ahd_unlock(ahd, &s);
        return;
    }

    tinfo = ahd_fetch_transinfo(ahd, SCSIID_CHANNEL(ahd, scb->hscb->scsiid),
                                SCSIID_OUR_ID(scb->hscb->scsiid),
                                SCSIID_TARGET(ahd, scb->hscb->scsiid),
                                &tstate);

    mask = SCB_GET_TARGET_MASK(ahd, scb);

    if ((tstate->discenable & mask) != 0)
        scb->hscb->control |= DISCENB;

    if ((tstate->tagenable & mask) != 0)
        scb->hscb->control |= TAG_ENB;

    if ((tinfo->curr.ppr_options & MSG_EXT_PPR_PROT_IUS) != 0) {
        scb->flags |= SCB_PACKETIZED;
        if (scb->hscb->task_management != 0)
            scb->hscb->control &= ~MK_MESSAGE;
    }

    if ((tstate->auto_negotiate & mask) != 0) {
        scb->flags |= SCB_AUTO_NEGOTIATE;
        scb->hscb->control |= MK_MESSAGE;
    }

    /* XXX with ahc there was some bus_dmamap_sync(PREREAD|PREWRITE); */

    LIST_INSERT_HEAD(&ahd->pending_scbs, scb, pending_links);

    if (!(xs->flags & SCSI_POLL))
        timeout_add_msec(&xs->stimeout, xs->timeout);

    scb->flags |= SCB_ACTIVE;

    if ((scb->flags & SCB_TARGET_IMMEDIATE) != 0) {
        /* Define a mapping from our tag to the SCB. */
        ahd->scb_data.scbindex[SCB_GET_TAG(scb)] = scb;
        ahd_pause(ahd);
        ahd_set_scbptr(ahd, SCB_GET_TAG(scb));
        ahd_outb(ahd, RETURN_1, CONT_MSG_LOOP_TARG);
        ahd_unpause(ahd);
    } else {
        ahd_queue_scb(ahd, scb);
    }

    if (!(xs->flags & SCSI_POLL)) {
        int target = xs->sc_link->target;
        int lun = SCB_GET_LUN(scb);

        if (ahd->inited_target[target] == 0) {
            struct  ahd_devinfo devinfo;

            ahd_adapter_req_set_xfer_mode(ahd, scb);
            ahd_compile_devinfo(&devinfo, ahd->our_id, target, lun,
                                'A', /*XXX milos*/ROLE_UNKNOWN);
            ahd_scb_devinfo(ahd, &devinfo, scb);
            ahd_update_neg_request(ahd, &devinfo, tstate, tinfo,
                                   AHD_NEG_IF_NON_ASYNC);
            ahd->inited_target[target] = 1;
        }

        ahd_unlock(ahd, &s);
        return;
    }

    /*
     * If we can't use interrupts, poll for completion
     */
    SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd_poll\n"));

    do {
        if (ahd_poll(ahd, xs->timeout)) {
            if (!(xs->flags & SCSI_SILENT))
                printf("cmd fail\n");
            ahd_timeout(scb);
            break;
        }
    } while (!(xs->flags & ITSDONE));

    ahd_unlock(ahd, &s);
}
Beispiel #16
0
/*
 * Called from the controller.
 */
int
fdintr(struct fdc_softc *fdc)
{
#define	st0	fdc->sc_status[0]
#define	cyl	fdc->sc_status[1]
	struct fd_softc *fd;
	struct buf *bp;
	bus_space_tag_t iot = fdc->sc_iot;
	bus_space_handle_t ioh = fdc->sc_ioh;
	bus_space_handle_t ioh_ctl = fdc->sc_ioh_ctl;
	int read, head, sec, i, nblks, cylin;
	struct fd_type *type;
	struct fd_formb *finfo = NULL;
	int fd_bsize;

loop:
	/* Is there a transfer to this drive?  If not, deactivate drive. */
	fd = TAILQ_FIRST(&fdc->sc_link.fdlink.sc_drives);
	if (fd == NULL) {
		fdc->sc_state = DEVIDLE;
		return 1;
	}
	fd_bsize = FD_BSIZE(fd);

	bp = fd->sc_bp;
	if (bp == NULL) {
		fd->sc_ops = 0;
		TAILQ_REMOVE(&fdc->sc_link.fdlink.sc_drives, fd, sc_drivechain);
		goto loop;
	}

	if (bp->b_flags & B_FORMAT)
	    finfo = (struct fd_formb *)bp->b_data;

	cylin = ((bp->b_blkno * DEV_BSIZE) + (bp->b_bcount - bp->b_resid)) /
	    (fd_bsize * fd->sc_type->seccyl);

	switch (fdc->sc_state) {
	case DEVIDLE:
		fdc->sc_errors = 0;
		fd->sc_skip = 0;
		fd->sc_bcount = bp->b_bcount;
		fd->sc_blkno = bp->b_blkno / (fd_bsize / DEV_BSIZE);
		timeout_del(&fd->fd_motor_off_to);
		if ((fd->sc_flags & FD_MOTOR_WAIT) != 0) {
			fdc->sc_state = MOTORWAIT;
			return 1;
		}
		if ((fd->sc_flags & FD_MOTOR) == 0) {
			/* Turn on the motor, being careful about pairing. */
			struct fd_softc *ofd =
				fdc->sc_link.fdlink.sc_fd[fd->sc_drive ^ 1];
			if (ofd && ofd->sc_flags & FD_MOTOR) {
				timeout_del(&ofd->fd_motor_off_to);
				ofd->sc_flags &= ~(FD_MOTOR | FD_MOTOR_WAIT);
			}
			fd->sc_flags |= FD_MOTOR | FD_MOTOR_WAIT;
			fd_set_motor(fdc, 0);
			fdc->sc_state = MOTORWAIT;
			/* Allow .25s for motor to stabilize. */
			timeout_add_msec(&fd->fd_motor_on_to, 250);
			return 1;
		}
		/* Make sure the right drive is selected. */
		fd_set_motor(fdc, 0);

		/* FALLTHROUGH */
	case DOSEEK:
	doseek:
		if (fd->sc_cylin == cylin)
			goto doio;

		out_fdc(iot, ioh, NE7CMD_SPECIFY);/* specify command */
		out_fdc(iot, ioh, fd->sc_type->steprate);
		out_fdc(iot, ioh, 6);		/* XXX head load time == 6ms */

		out_fdc(iot, ioh, NE7CMD_SEEK);	/* seek function */
		out_fdc(iot, ioh, fd->sc_drive);	/* drive number */
		out_fdc(iot, ioh, cylin * fd->sc_type->step);

		fd->sc_cylin = -1;
		fdc->sc_state = SEEKWAIT;

		fd->sc_dk.dk_seek++;
		disk_busy(&fd->sc_dk);

		timeout_add_sec(&fd->fdtimeout_to, 4);
		return 1;

	case DOIO:
	doio:
		type = fd->sc_type;
		if (finfo)
		    fd->sc_skip = (char *)&(finfo->fd_formb_cylno(0)) -
			(char *)finfo;
		sec = fd->sc_blkno % type->seccyl;
		nblks = type->seccyl - sec;
		nblks = min(nblks, fd->sc_bcount / fd_bsize);
		nblks = min(nblks, FDC_MAXIOSIZE / fd_bsize);
		fd->sc_nblks = nblks;
		fd->sc_nbytes = finfo ? bp->b_bcount : nblks * fd_bsize;
		head = sec / type->sectrac;
		sec -= head * type->sectrac;
#ifdef DIAGNOSTIC
		{int block;
		 block = (fd->sc_cylin * type->heads + head) * type->sectrac + sec;
		 if (block != fd->sc_blkno) {
			 panic("fdintr: block %d != blkno %llu", block, fd->sc_blkno);
		 }}
#endif
		read = bp->b_flags & B_READ ? DMAMODE_READ : DMAMODE_WRITE;
		isadma_start(bp->b_data + fd->sc_skip, fd->sc_nbytes,
		    fdc->sc_drq, read);
		bus_space_write_1(iot, ioh_ctl, fdctl, type->rate);
#ifdef FD_DEBUG
		printf("fdintr: %s drive %d track %d head %d sec %d nblks %d\n",
		    read ? "read" : "write", fd->sc_drive, fd->sc_cylin, head,
		    sec, nblks);
#endif
		if (finfo) {
                        /* formatting */
			if (out_fdc(iot, ioh, NE7CMD_FORMAT) < 0) {
			    fdc->sc_errors = 4;
			    fdretry(fd);
			    goto loop;
			}
                        out_fdc(iot, ioh, (head << 2) | fd->sc_drive);
                        out_fdc(iot, ioh, finfo->fd_formb_secshift);
                        out_fdc(iot, ioh, finfo->fd_formb_nsecs);
                        out_fdc(iot, ioh, finfo->fd_formb_gaplen);
                        out_fdc(iot, ioh, finfo->fd_formb_fillbyte);
		} else {
			if (read)
				out_fdc(iot, ioh, NE7CMD_READ);	/* READ */
			else
				out_fdc(iot, ioh, NE7CMD_WRITE);/* WRITE */
			out_fdc(iot, ioh, (head << 2) | fd->sc_drive);
			out_fdc(iot, ioh, fd->sc_cylin);	/* track */
			out_fdc(iot, ioh, head);
			out_fdc(iot, ioh, sec + 1);		/* sec +1 */
			out_fdc(iot, ioh, type->secsize);	/* sec size */
			out_fdc(iot, ioh, type->sectrac);	/* secs/track */
			out_fdc(iot, ioh, type->gap1);		/* gap1 size */
			out_fdc(iot, ioh, type->datalen);	/* data len */
		}
		fdc->sc_state = IOCOMPLETE;

		disk_busy(&fd->sc_dk);

		/* allow 2 seconds for operation */
		timeout_add_sec(&fd->fdtimeout_to, 2);
		return 1;				/* will return later */

	case SEEKWAIT:
		timeout_del(&fd->fdtimeout_to);
		fdc->sc_state = SEEKCOMPLETE;
		/* allow 1/50 second for heads to settle */
		timeout_add_msec(&fdc->fdcpseudointr_to, 20);
		return 1;

	case SEEKCOMPLETE:
		disk_unbusy(&fd->sc_dk, 0, 0);	/* no data on seek */

		/* Make sure seek really happened. */
		out_fdc(iot, ioh, NE7CMD_SENSEI);
		if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 ||
		    cyl != cylin * fd->sc_type->step) {
#ifdef FD_DEBUG
			fdcstatus(&fd->sc_dev, 2, "seek failed");
#endif
			fdretry(fd);
			goto loop;
		}
		fd->sc_cylin = cylin;
		goto doio;

	case IOTIMEDOUT:
		isadma_abort(fdc->sc_drq);
	case SEEKTIMEDOUT:
	case RECALTIMEDOUT:
	case RESETTIMEDOUT:
		fdretry(fd);
		goto loop;

	case IOCOMPLETE: /* IO DONE, post-analyze */
		timeout_del(&fd->fdtimeout_to);

		disk_unbusy(&fd->sc_dk, (bp->b_bcount - bp->b_resid),
		    (bp->b_flags & B_READ));

		if (fdcresult(fdc) != 7 || (st0 & 0xf8) != 0) {
			isadma_abort(fdc->sc_drq);
#ifdef FD_DEBUG
			fdcstatus(&fd->sc_dev, 7, bp->b_flags & B_READ ?
			    "read failed" : "write failed");
			printf("blkno %lld nblks %d\n",
			    (long long)fd->sc_blkno, fd->sc_nblks);
#endif
			fdretry(fd);
			goto loop;
		}
		read = bp->b_flags & B_READ ? DMAMODE_READ : DMAMODE_WRITE;
		isadma_done(fdc->sc_drq);
		if (fdc->sc_errors) {
			diskerr(bp, "fd", "soft error", LOG_PRINTF,
			    fd->sc_skip / fd_bsize, (struct disklabel *)NULL);
			printf("\n");
			fdc->sc_errors = 0;
		}

		fd->sc_blkno += fd->sc_nblks;
		fd->sc_skip += fd->sc_nbytes;
		fd->sc_bcount -= fd->sc_nbytes;
		bp->b_resid -= fd->sc_nbytes;
		if (!finfo && fd->sc_bcount > 0) {
			cylin = fd->sc_blkno / fd->sc_type->seccyl;
			goto doseek;
		}
		fdfinish(fd, bp);
		goto loop;

	case DORESET:
		/* try a reset, keep motor on */
		fd_set_motor(fdc, 1);
		delay(100);
		fd_set_motor(fdc, 0);
		fdc->sc_state = RESETCOMPLETE;
		timeout_add_msec(&fd->fdtimeout_to, 500);
		return 1;			/* will return later */

	case RESETCOMPLETE:
		timeout_del(&fd->fdtimeout_to);
		/* clear the controller output buffer */
		for (i = 0; i < 4; i++) {
			out_fdc(iot, ioh, NE7CMD_SENSEI);
			(void) fdcresult(fdc);
		}

		/* FALLTHROUGH */
	case DORECAL:
		out_fdc(iot, ioh, NE7CMD_RECAL);	/* recal function */
		out_fdc(iot, ioh, fd->sc_drive);
		fdc->sc_state = RECALWAIT;
		timeout_add_sec(&fd->fdtimeout_to, 5);
		return 1;			/* will return later */

	case RECALWAIT:
		timeout_del(&fd->fdtimeout_to);
		fdc->sc_state = RECALCOMPLETE;
		/* allow 1/30 second for heads to settle */
		timeout_add(&fdc->fdcpseudointr_to, hz / 30);
		return 1;			/* will return later */

	case RECALCOMPLETE:
		out_fdc(iot, ioh, NE7CMD_SENSEI);
		if (fdcresult(fdc) != 2 || (st0 & 0xf8) != 0x20 || cyl != 0) {
#ifdef FD_DEBUG
			fdcstatus(&fd->sc_dev, 2, "recalibrate failed");
#endif
			fdretry(fd);
			goto loop;
		}
		fd->sc_cylin = 0;
		goto doseek;

	case MOTORWAIT:
		if (fd->sc_flags & FD_MOTOR_WAIT)
			return 1;		/* time's not up yet */
		goto doseek;

	default:
		fdcstatus(&fd->sc_dev, 0, "stray interrupt");
		return 1;
	}
#ifdef DIAGNOSTIC
	panic("fdintr: impossible");
#endif
#undef	st0
#undef	cyl
}