struct ahc_dma_seg *sg;
u_int active_scb_index;  active_scb_index = ahc_inb(ahc, SCB_TAG);  if (last_phase != P_BUSFREE && (active_scb_index < ahc->scb_data->numscbs)) { struct scb *active_scb;   active_scb = &ahc->scb_data->scbarray[active_scb_index]; if (active_scb->hscb->tcl != scb->hscb->tcl) { u_int	newtimeout;  sc_print_addr(scb->xs->sc_link); printf("Other SCB Timeout%s", (scb->flags & SCB_OTHERTCL_TIMEOUT) != 0 ? " again\n" : "\n"); scb->flags |= SCB_OTHERTCL_TIMEOUT; newtimeout = MAX(active_scb->xs->timeout, scb->xs->timeout); timeout_add(&scb->xs->stimeout, (newtimeout * hz) / 1000); splx(s); return; }   if ((scb->hscb->control & TARGET_SCB) != 0) {   ahc_freeze_devq(ahc, scb->xs->sc_link); ahcsetccbstatus(scb->xs, XS_TIMEOUT); ahc_freeze_ccb(scb); ahc_done(ahc, scb);   restart_sequencer(ahc); splx(s); return; }  ahc_set_recoveryscb(ahc, active_scb); ahc_outb(ahc, MSG_OUT, MSG_BUS_DEV_RESET); ahc_outb(ahc, SCSISIGO, last_phase|ATNO); sc_print_addr(active_scb->xs->sc_link); printf("BDR message in message buffer\n"); active_scb->flags |=  SCB_DEVICE_RESET; timeout_add(&active_scb->xs->stimeout, 2 * hz); unpause_sequencer(ahc); } else { int	 disconnected;   if ((scb->hscb->control & TARGET_SCB) != 0) panic("Timed-out target SCB but bus idle");  if (last_phase != P_BUSFREE && (ahc_inb(ahc, SSTAT0) & TARGET) != 0) {   printf("%s: Hung target selection\n", ahc_name(ahc)); restart_sequencer(ahc); splx(s); return; }  if (ahc_search_qinfifo(ahc, target, channel, lun, scb->hscb->tag, ROLE_INITIATOR, 0, SEARCH_COUNT) > 0) { disconnected = FALSE; } else { disconnected = TRUE; }  if (disconnected) { u_int active_scb;  ahc_set_recoveryscb(ahc, scb);  scb->hscb->control |= MK_MESSAGE; scb->flags |= SCB_QUEUED_MSG |  SCB_DEVICE_RESET;   active_scb = ahc_inb(ahc, SCBPTR); if (ahc_search_disc_list(ahc, target, channel, lun, scb->hscb->tag, TRUE, FALSE, FALSE)) { u_int scb_control;  scb_control = ahc_inb(ahc, SCB_CONTROL); scb_control |= MK_MESSAGE; ahc_outb(ahc, SCB_CONTROL, scb_control); } ahc_outb(ahc, SCBPTR, active_scb); ahc_index_busy_tcl(ahc, scb->hscb->tcl, TRUE);   ahc_search_qinfifo(ahc, SCB_TARGET(scb), channel, SCB_LUN(scb), SCB_LIST_NULL, ROLE_INITIATOR, SCB_REQUEUE, SEARCH_COMPLETE); sc_print_addr(scb->xs->sc_link); printf("Queuing a BDR SCB\n"); ahc->qinfifo[ahc->qinfifonext++] = scb->hscb->tag;  bus_dmamap_sync(ahc->sc_dmat, ahc->shared_data_dmamap, QINFIFO_OFFSET * 256, 256, BUS_DMASYNC_PREWRITE);  if ((ahc->features & AHC_QUEUE_REGS) != 0) { ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext); } else { ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext); } timeout_add(&scb->xs->stimeout, 2 * hz); unpause_sequencer(ahc); } else {   ahc_set_recoveryscb(ahc, scb); sc_print_addr(scb->xs->sc_link); printf("SCB %d: Immediate reset.  " "Flags = 0x%x\n", scb->hscb->tag, scb->flags); goto bus_reset; } } } splx(s); }  STATIC int ahc_search_qinfifo(ahc, target, channel, lun, tag, role, status, action) struct ahc_softc *ahc; int target; char channel; int lun; u_int tag; role_t role; u_int32_t status; ahc_search_action action; { struct	 scb *scbp; u_int8_t qinpos; u_int8_t qintail; int	 found;  qinpos = ahc_inb(ahc, QINPOS); qintail = ahc->qinfifonext; found = 0;   ahc->qinfifonext = qinpos; bus_dmamap_sync(ahc->sc_dmat, ahc->shared_data_dmamap, QINFIFO_OFFSET * 256, 256, BUS_DMASYNC_POSTREAD);  while (qinpos != qintail) { scbp = &ahc->scb_data->scbarray[ahc->qinfifo[qinpos]]; if (ahc_match_scb(scbp, target, channel, lun, tag, role)) {  switch (action) { case SEARCH_COMPLETE: if (!(scbp->xs->flags & ITSDONE)) { scbp->flags |= status; scbp->xs->error = XS_NOERROR; } ahc_freeze_ccb(scbp); ahc_done(ahc, scbp); break; case SEARCH_COUNT: ahc->qinfifo[ahc->qinfifonext++] = scbp->hscb->tag; break; case SEARCH_REMOVE: break; } found++; } else { ahc->qinfifo[ahc->qinfifonext++] = scbp->hscb->tag; } qinpos++; } bus_dmamap_sync(ahc->sc_dmat, ahc->shared_data_dmamap, QINFIFO_OFFSET * 256, 256, BUS_DMASYNC_PREWRITE);  if ((ahc->features & AHC_QUEUE_REGS) != 0) { ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext); } else { ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext); }  return (found); }   STATIC int ahc_abort_scbs(ahc, target, channel, lun, tag, role, status) struct ahc_softc *ahc; int target; char channel; int lun; u_int tag; role_t role; u_int32_t status; { struct	scb *scbp; u_int	active_scb; int	i; int	found;   active_scb = ahc_inb(ahc, SCBPTR);  found = ahc_search_qinfifo(ahc, target, channel, lun, SCB_LIST_NULL, role, SCB_REQUEUE, SEARCH_COMPLETE);   { u_int8_t next, prev;  next = ahc_inb(ahc, WAITING_SCBH); prev = SCB_LIST_NULL;  while (next != SCB_LIST_NULL) { u_int8_t scb_index;  ahc_outb(ahc, SCBPTR, next); scb_index = ahc_inb(ahc, SCB_TAG); if (scb_index >= ahc->scb_data->numscbs) { panic("Waiting List inconsistency. " "SCB index == %d, yet numscbs == %d.", scb_index, ahc->scb_data->numscbs); } scbp = &ahc->scb_data->scbarray[scb_index]; if (ahc_match_scb(scbp, target, channel, lun, SCB_LIST_NULL, role)) {  next = ahc_abort_wscb(ahc, next, prev); } else {  prev = next; next = ahc_inb(ahc, SCB_NEXT); } } }  ahc_search_disc_list(ahc, target, channel, lun, tag, FALSE, TRUE, FALSE);   for(i = 0; i < ahc->scb_data->maxhscbs; i++) { u_int scbid;  ahc_outb(ahc, SCBPTR, i); scbid = ahc_inb(ahc, SCB_TAG); scbp = &ahc->scb_data->scbarray[scbid]; if (scbid < ahc->scb_data->numscbs && ahc_match_scb(scbp, target, channel, lun, tag, role)) ahc_add_curscb_to_free_list(ahc); }   { struct scb *scb;  scb = ahc->pending_scbs.lh_first; while (scb != NULL) { scbp = scb; scb = scb->pend_links.le_next; if (ahc_match_scb(scbp, target, channel, lun, tag, role)) { if (!(scbp->xs->flags & ITSDONE)) ahcsetccbstatus(scbp->xs, status); ahc_freeze_ccb(scbp); ahc_done(ahc, scbp); found++; } } } ahc_outb(ahc, SCBPTR, active_scb); return found; }  STATIC int ahc_search_disc_list(ahc, target, channel, lun, tag, stop_on_first, remove, save_state) struct ahc_softc *ahc; int target; char channel; int lun; u_int tag; int stop_on_first; int remove; int save_state; { struct	scb *scbp; u_int	next; u_int	prev; u_int	count; u_int	active_scb;  count = 0; next = ahc_inb(ahc, DISCONNECTED_SCBH); prev = SCB_LIST_NULL;  if (save_state) {  active_scb = ahc_inb(ahc, SCBPTR); } else  active_scb = SCB_LIST_NULL;  while (next != SCB_LIST_NULL) { u_int scb_index;  ahc_outb(ahc, SCBPTR, next); scb_index = ahc_inb(ahc, SCB_TAG); if (scb_index >= ahc->scb_data->numscbs) { panic("Disconnected List inconsistency. " "SCB index == %d, yet numscbs == %d.", scb_index, ahc->scb_data->numscbs); } scbp = &ahc->scb_data->scbarray[scb_index]; if (ahc_match_scb(scbp, target, channel, lun, tag, ROLE_INITIATOR)) { count++; if (remove) { next = ahc_rem_scb_from_disc_list(ahc, prev, next); } else { prev = next; next = ahc_inb(ahc, SCB_NEXT); } if (stop_on_first) break; } else { prev = next; next = ahc_inb(ahc, SCB_NEXT); } } if (save_state) ahc_outb(ahc, SCBPTR, active_scb); return (count); }  STATIC u_int ahc_rem_scb_from_disc_list(ahc, prev, scbptr) struct ahc_softc *ahc; u_int prev; u_int scbptr; { u_int next;  ahc_outb(ahc, SCBPTR, scbptr); next = ahc_inb(ahc, SCB_NEXT);  ahc_outb(ahc, SCB_CONTROL, 0);  ahc_add_curscb_to_free_list(ahc);  if (prev != SCB_LIST_NULL) { ahc_outb(ahc, SCBPTR, prev); ahc_outb(ahc, SCB_NEXT, next); } else ahc_outb(ahc, DISCONNECTED_SCBH, next);  return (next); }  STATIC void ahc_add_curscb_to_free_list(ahc) struct ahc_softc *ahc; {  ahc_outb(ahc, SCB_TAG, SCB_LIST_NULL);  ahc_outb(ahc, SCB_NEXT, ahc_inb(ahc, FREE_SCBH)); ahc_outb(ahc, FREE_SCBH, ahc_inb(ahc, SCBPTR)); }   STATIC u_int ahc_abort_wscb(ahc, scbpos, prev) struct ahc_softc *ahc; u_int scbpos; u_int prev; { u_int curscb, next;   curscb = ahc_inb(ahc, SCBPTR); ahc_outb(ahc, SCBPTR, scbpos); next = ahc_inb(ahc, SCB_NEXT);   ahc_outb(ahc, SCB_CONTROL, 0);  ahc_add_curscb_to_free_list(ahc);   if (prev == SCB_LIST_NULL) {  ahc_outb(ahc, WAITING_SCBH, next);   ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO)); } else {  ahc_outb(ahc, SCBPTR, prev); ahc_outb(ahc, SCB_NEXT, next); }   ahc_outb(ahc, SCBPTR, curscb); return next; }  STATIC void ahc_clear_intstat(ahc) struct ahc_softc *ahc; {  ahc_outb(ahc, CLRSINT0, CLRSELDO|CLRSELDI|CLRSELINGO); ahc_outb(ahc, CLRSINT1, CLRSELTIMEO|CLRATNO|CLRSCSIRSTI |CLRBUSFREE|CLRSCSIPERR|CLRPHASECHG| CLRREQINIT); ahc_outb(ahc, CLRINT, CLRSCSIINT); }  STATIC void ahc_reset_current_bus(ahc) struct ahc_softc *ahc; { u_int8_t scsiseq;  ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENSCSIRST); scsiseq = ahc_inb(ahc, SCSISEQ); ahc_outb(ahc, SCSISEQ, scsiseq | SCSIRSTO); DELAY(AHC_BUSRESET_DELAY);  ahc_outb(ahc, SCSISEQ, scsiseq & ~SCSIRSTO);  ahc_clear_intstat(ahc);   ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) | ENSCSIRST); }  STATIC int ahc_reset_channel(ahc, channel, initiate_reset) struct ahc_softc *ahc; char channel; int initiate_reset; { u_int	initiator, target, max_scsiid; u_int	sblkctl; u_int	our_id; int	found; int	restart_needed; char	cur_channel;  ahc->pending_device = NULL;  pause_sequencer(ahc);   ahc_run_qoutfifo(ahc);   sblkctl = ahc_inb(ahc, SBLKCTL); cur_channel = 'A'; if ((ahc->features & AHC_TWIN) != 0 && ((sblkctl & SELBUSB) != 0)) cur_channel = 'B'; if (cur_channel != channel) {  ahc_outb(ahc, SBLKCTL, sblkctl ^ SELBUSB); ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE); ahc_outb(ahc, SCSISEQ, ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP)); if (initiate_reset) ahc_reset_current_bus(ahc); ahc_clear_intstat(ahc); ahc_outb(ahc, SBLKCTL, sblkctl); restart_needed = FALSE; } else {  ahc_clear_msg_state(ahc); ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE); ahc_outb(ahc, SCSISEQ, ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP)); if (initiate_reset) ahc_reset_current_bus(ahc); ahc_clear_intstat(ahc);   ahc->qoutfifonext = 0; if ((ahc->features & AHC_QUEUE_REGS) != 0) ahc_outb(ahc, SDSCB_QOFF, 0); else ahc_outb(ahc, QOUTPOS, 0); restart_needed = TRUE; }   found = ahc_abort_scbs(ahc, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL, ROLE_UNKNOWN, XS_RESET); if (channel == 'B') { our_id = ahc->our_id_b; } else { our_id = ahc->our_id; }  max_scsiid = (ahc->features & AHC_WIDE) ? 15 : 7;   for (target = 0; target <= max_scsiid; target++) {  if (ahc->enabled_targets[target] == NULL) continue; for (initiator = 0; initiator <= max_scsiid; initiator++) { struct ahc_devinfo devinfo;  ahc_compile_devinfo(&devinfo, target, initiator, ALL_LUNS, channel, ROLE_UNKNOWN); ahc_set_width(ahc, &devinfo, MSG_EXT_WDTR_BUS_8_BIT, AHC_TRANS_CUR, TRUE, FALSE); ahc_set_syncrate(ahc, &devinfo, NULL, 0, 0, AHC_TRANS_CUR, TRUE, FALSE); } }  if (restart_needed) restart_sequencer(ahc); else unpause_sequencer(ahc); return found; }  STATIC int ahc_match_scb(scb, target, channel, lun, role, tag) struct scb *scb; int target; char channel; int lun; role_t role; u_int tag; { int targ = SCB_TARGET(scb); char chan = SCB_CHANNEL(scb); int slun = SCB_LUN(scb); int match;  match = ((chan == channel) || (channel == ALL_CHANNELS)); if (match != 0) match = ((targ == target) || (target == ALL_TARGETS)); if (match != 0) match = ((lun == slun) || (lun == ALL_LUNS)); return match; }  STATIC void ahc_construct_sdtr(ahc, period, offset) struct ahc_softc *ahc; u_int period; u_int offset; { ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED; ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_SDTR_LEN; ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_SDTR; ahc->msgout_buf[ahc->msgout_index++] = period; ahc->msgout_buf[ahc->msgout_index++] = offset; ahc->msgout_len += 5; }  STATIC void ahc_construct_wdtr(ahc, bus_width) struct ahc_softc *ahc; u_int bus_width; { ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED; ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_WDTR_LEN; ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_WDTR; ahc->msgout_buf[ahc->msgout_index++] = bus_width; ahc->msgout_len += 4; }  STATIC void ahc_calc_residual(scb) struct scb *scb; { struct	hardware_scb *hscb;  hscb = scb->hscb;   if ((scb->hscb->control & DISCONNECTED) == 0) { u_int32_t resid; int	  resid_sgs; int	  sg;
ahc_calc_residual(struct scb *scb){	struct hardware_scb *hscb;	struct status_pkt *spkt;	uint32_t sgptr;	uint32_t resid_sgptr;	uint32_t resid;	/*	 * 5 cases.	 * 1) No residual.	 *    SG_RESID_VALID clear in sgptr.	 * 2) Transferless command	 * 3) Never performed any transfers.	 *    sgptr has SG_FULL_RESID set.	 * 4) No residual but target did not	 *    save data pointers after the	 *    last transfer, so sgptr was	 *    never updated.	 * 5) We have a partial residual.	 *    Use residual_sgptr to determine	 *    where we are.	 */	hscb = scb->hscb;	sgptr = ahc_le32toh(hscb->sgptr);	if ((sgptr & SG_RESID_VALID) == 0)		/* Case 1 */		return;	sgptr &= ~SG_RESID_VALID;	if ((sgptr & SG_LIST_NULL) != 0)		/* Case 2 */		return;	spkt = &hscb->shared_data.status;	resid_sgptr = ahc_le32toh(spkt->residual_sg_ptr);	if ((sgptr & SG_FULL_RESID) != 0) {		/* Case 3 */		resid = ahc_get_transfer_length(scb);	} else if ((resid_sgptr & SG_LIST_NULL) != 0) {		/* Case 4 */		return;	} else if ((resid_sgptr & ~SG_PTR_MASK) != 0) {		panic("Bogus resid sgptr value 0x%x\n", resid_sgptr);	} else {		struct ahc_dma_seg *sg;		/*		 * Remainder of the SG where the transfer		 * stopped.  		 */		resid = ahc_le32toh(spkt->residual_datacnt) & AHC_SG_LEN_MASK;		sg = ahc_sg_bus_to_virt(scb, resid_sgptr & SG_PTR_MASK);		/* The residual sg_ptr always points to the next sg */		sg--;		/*		 * Add up the contents of all residual		 * SG segments that are after the SG where		 * the transfer stopped.		 */		while ((ahc_le32toh(sg->len) & AHC_DMA_LAST_SEG) == 0) {			sg++;			resid += ahc_le32toh(sg->len) & AHC_SG_LEN_MASK;		}	}	if ((scb->flags & SCB_SENSE) == 0)		ahc_set_residual(scb, resid);	else		ahc_set_sense_residual(scb, resid);#ifdef AHC_DEBUG	if (ahc_debug & AHC_SHOWMISC) {		ahc_print_path(scb->ahc_softc, scb);		printf("Handled Residual of %d bytes\n", resid);	}#endif}