/******************************************************************************** * Bring the controller down to a dormant state and detach all child devices. * * Note that we can assume that the bioq on the controller is empty, as we won't * allow shutdown if any device is open. */ static int twe_shutdown(device_t dev) { struct twe_softc *sc = device_get_softc(dev); int i, error = 0; debug_called(4); /* * Delete all our child devices. */ TWE_CONFIG_LOCK(sc); for (i = 0; i < TWE_MAX_UNITS; i++) { if (sc->twe_drive[i].td_disk != 0) { if ((error = twe_detach_drive(sc, i)) != 0) { TWE_CONFIG_UNLOCK(sc); return (error); } } } TWE_CONFIG_UNLOCK(sc); /* * Bring the controller down. */ TWE_IO_LOCK(sc); twe_deinit(sc); TWE_IO_UNLOCK(sc); return(0); }
/******************************************************************************** * Handle an I/O request. */ static void twed_strategy(struct bio *bp) { struct twed_softc *sc = bp->bio_disk->d_drv1; debug_called(4); bp->bio_driver1 = &sc->twed_drive->td_twe_unit; TWED_BIO_IN; /* bogus disk? */ if (sc == NULL || sc->twed_drive->td_disk == NULL) { bp->bio_error = EINVAL; bp->bio_flags |= BIO_ERROR; printf("twe: bio for invalid disk!\n"); biodone(bp); TWED_BIO_OUT; return; } /* queue the bio on the controller */ TWE_IO_LOCK(sc->twed_controller); twe_enqueue_bio(sc->twed_controller, bp); /* poke the controller to start I/O */ twe_startio(sc->twed_controller); TWE_IO_UNLOCK(sc->twed_controller); return; }
/******************************************************************************** * Disconnect from the controller completely, in preparation for unload. */ static int twe_detach(device_t dev) { struct twe_softc *sc = device_get_softc(dev); debug_called(4); TWE_IO_LOCK(sc); if (sc->twe_state & TWE_STATE_OPEN) { TWE_IO_UNLOCK(sc); return (EBUSY); } sc->twe_state |= TWE_STATE_DETACHING; TWE_IO_UNLOCK(sc); /* * Shut the controller down. */ if (twe_shutdown(dev)) { TWE_IO_LOCK(sc); sc->twe_state &= ~TWE_STATE_DETACHING; TWE_IO_UNLOCK(sc); return (EBUSY); } twe_free(sc); return(0); }
/******************************************************************************** * Handle an I/O request. */ static void twed_strategy(twe_bio *bp) { struct twed_softc *sc = (struct twed_softc *)TWE_BIO_SOFTC(bp); debug_called(4); TWED_BIO_IN; /* bogus disk? */ if (sc == NULL) { TWE_BIO_SET_ERROR(bp, EINVAL); printf("twe: bio for invalid disk!\n"); TWE_BIO_DONE(bp); TWED_BIO_OUT; return; } /* perform accounting */ TWE_BIO_STATS_START(bp); /* queue the bio on the controller */ twe_enqueue_bio(sc->twed_controller, bp); /* poke the controller to start I/O */ twe_startio(sc->twed_controller); return; }
/******************************************************************************** * Detach from CAM */ void mly_cam_detach(struct mly_softc *sc) { int chn, nchn, first; debug_called(1); nchn = sc->mly_controllerinfo->physical_channels_present + sc->mly_controllerinfo->virtual_channels_present; /* * Iterate over channels, deregistering as we go. */ nchn = sc->mly_controllerinfo->physical_channels_present + sc->mly_controllerinfo->virtual_channels_present; for (chn = 0, first = 1; chn < nchn; chn++) { /* * If a sim was registered for this channel, free it. */ if (sc->mly_cam_sim[chn] != NULL) { debug(1, "deregister bus %d", chn); xpt_bus_deregister(cam_sim_path(sc->mly_cam_sim[chn])); debug(1, "free sim for channel %d (%sfree queue)", chn, first ? "" : "don't "); cam_sim_free(sc->mly_cam_sim[chn], first ? TRUE : FALSE); first = 0; } } }
/* * Read/write routine for a buffer. Finds the proper unit, range checks * arguments, and schedules the transfer. Does not wait for the transfer * to complete. Multi-page transfers are supported. All I/O requests must * be a multiple of a sector in length. */ static void mlxd_strategy(struct bio *bp) { struct mlxd_softc *sc = bp->bio_disk->d_drv1; debug_called(1); /* bogus disk? */ if (sc == NULL) { bp->bio_error = EINVAL; bp->bio_flags |= BIO_ERROR; goto bad; } /* XXX may only be temporarily offline - sleep? */ MLX_IO_LOCK(sc->mlxd_controller); if (sc->mlxd_drive->ms_state == MLX_SYSD_OFFLINE) { MLX_IO_UNLOCK(sc->mlxd_controller); bp->bio_error = ENXIO; bp->bio_flags |= BIO_ERROR; goto bad; } mlx_submit_buf(sc->mlxd_controller, bp); MLX_IO_UNLOCK(sc->mlxd_controller); return; bad: /* * Correctly set the bio to indicate a failed tranfer. */ bp->bio_resid = bp->bio_bcount; biodone(bp); return; }
/******************************************************************************** * Bring the controller down to a dormant state and detach all child devices. * * Note that we can assume that the bioq on the controller is empty, as we won't * allow shutdown if any device is open. */ static int twe_shutdown(device_t dev) { struct twe_softc *sc = device_get_softc(dev); int i, s, error; debug_called(4); s = splbio(); error = 0; /* * Delete all our child devices. */ for (i = 0; i < TWE_MAX_UNITS; i++) { if (sc->twe_drive[i].td_disk != 0) { if ((error = device_delete_child(sc->twe_dev, sc->twe_drive[i].td_disk)) != 0) goto out; sc->twe_drive[i].td_disk = 0; } } /* * Bring the controller down. */ twe_deinit(sc); out: splx(s); return(error); }
/******************************************************************************** * Bring the controller down to a dormant state and detach all child devices. * * This function is called before detach, system shutdown, or before performing * an operation which may add or delete system disks. (Call amr_startup to * resume normal operation.) * * Note that we can assume that the bioq on the controller is empty, as we won't * allow shutdown if any device is open. */ static int amr_pci_shutdown(device_t dev) { struct amr_softc *sc = device_get_softc(dev); int i,error; debug_called(1); /* mark ourselves as in-shutdown */ sc->amr_state |= AMR_STATE_SHUTDOWN; /* flush controller */ device_printf(sc->amr_dev, "flushing cache..."); printf("%s\n", amr_flush(sc) ? "failed" : "done"); error = 0; /* delete all our child devices */ for(i = 0 ; i < AMR_MAXLD; i++) { if( sc->amr_drive[i].al_disk != 0) { if((error = device_delete_child(sc->amr_dev,sc->amr_drive[i].al_disk)) != 0) goto shutdown_out; sc->amr_drive[i].al_disk = 0; } } /* XXX disable interrupts? */ shutdown_out: return(error); }
/******************************************************************************** * Free all of the resources associated with (sc). * * Should not be called if the controller is active. */ static void twe_free(struct twe_softc *sc) { struct twe_request *tr; debug_called(4); /* throw away any command buffers */ while ((tr = twe_dequeue_free(sc)) != NULL) twe_free_request(tr); /* destroy the data-transfer DMA tag */ if (sc->twe_buffer_dmat) bus_dma_tag_destroy(sc->twe_buffer_dmat); /* disconnect the interrupt handler */ if (sc->twe_intr) bus_teardown_intr(sc->twe_dev, sc->twe_irq, sc->twe_intr); if (sc->twe_irq != NULL) bus_release_resource(sc->twe_dev, SYS_RES_IRQ, 0, sc->twe_irq); /* destroy the parent DMA tag */ if (sc->twe_parent_dmat) bus_dma_tag_destroy(sc->twe_parent_dmat); /* release the register window mapping */ if (sc->twe_io != NULL) bus_release_resource(sc->twe_dev, SYS_RES_IOPORT, TWE_IO_CONFIG_REG, sc->twe_io); /* destroy control device */ if (sc->twe_dev_t != (dev_t)NULL) destroy_dev(sc->twe_dev_t); }
/******************************************************************************** * Disconnect from the controller completely, in preparation for unload. */ static int twe_detach(device_t dev) { struct twe_softc *sc = device_get_softc(dev); int s, error; debug_called(4); error = EBUSY; s = splbio(); if (sc->twe_state & TWE_STATE_OPEN) goto out; /* * Shut the controller down. */ if ((error = twe_shutdown(dev))) goto out; twe_free(sc); error = 0; out: splx(s); return(error); }
static void _inq(struct cam_sim *sim, union ccb *ccb) { struct ccb_pathinq *cpi = &ccb->cpi; isc_session_t *sp = cam_sim_softc(sim); debug_called(8); debug(3, "sid=%d target=%d lun=%d", sp->sid, ccb->ccb_h.target_id, ccb->ccb_h.target_lun); cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = PI_SDTR_ABLE | PI_TAG_ABLE | PI_WIDE_32; cpi->target_sprt = 0; cpi->hba_misc = 0; cpi->hba_eng_cnt = 0; cpi->max_target = 0; //ISCSI_MAX_TARGETS - 1; cpi->initiator_id = ISCSI_MAX_TARGETS; cpi->max_lun = sp->opt.maxluns - 1; cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 3300; // 40000; // XXX: strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "iSCSI", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->ccb_h.status = CAM_REQ_CMP; #if defined(KNOB_VALID_ADDRESS) cpi->transport = XPORT_ISCSI; cpi->transport_version = 0; #endif }
/* | this is a kludge, | the jury is not back with a veredict, user or kernel */ static void _nop_out(isc_session_t *sp) { pduq_t *pq; nop_out_t *nop_out; debug_called(8); sdebug(4, "cws=%d", sp->cws); if(sp->cws == 0) { /* | only send a nop if window is closed. */ if((pq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL) // I guess we ran out of resources return; nop_out = &pq->pdu.ipdu.nop_out; nop_out->opcode = ISCSI_NOP_OUT; nop_out->itt = htonl(sp->sn.itt); nop_out->ttt = -1; nop_out->I = 1; nop_out->F = 1; if(isc_qout(sp, pq) != 0) { sdebug(1, "failed"); pdu_free(sp->isc, pq); } } }
static int ic_scan(isc_session_t *sp) { union ccb *ccb; debug_called(8); sdebug(2, "scanning sid=%d", sp->sid); sp->flags &= ~ISC_CAMDEVS; sp->flags |= ISC_SCANWAIT; ccb = xpt_alloc_ccb(); ccb->ccb_h.path = sp->cam_path; ccb->ccb_h.cbfcnp = scan_callback; ccb->ccb_h.spriv_ptr0 = sp; xpt_rescan(ccb); while(sp->flags & ISC_SCANWAIT) tsleep(sp, PRIBIO, "ffp", 5*hz); // the timeout time should // be configurable sdebug(2, "# of luns=%d", sp->target_nluns); if(sp->target_nluns > 0) { sp->flags |= ISC_CAMDEVS; return 0; } return ENODEV; }
static int chapSHA1(char id, char *cp, char *chapSecret, unsigned char *digest) { SHA1_CTX ctx; char *tmp; int len; debug_called(3); SHA1_Init(&ctx); SHA1_Update(&ctx, &id, 1); if((len = str2bin(chapSecret, &tmp)) == 0) { // print error return -1; } SHA1_Update(&ctx, tmp, len); free(tmp); if((len = str2bin(cp, &tmp)) == 0) { // print error return -1; } SHA1_Update(&ctx, tmp, len); free(tmp); SHA1_Final(digest, &ctx); return 0; }
/* * Read/write routine for a buffer. Finds the proper unit, range checks * arguments, and schedules the transfer. Does not wait for the transfer * to complete. Multi-page transfers are supported. All I/O requests must * be a multiple of a sector in length. */ static void mlxd_strategy(mlx_bio *bp) { struct mlxd_softc *sc = (struct mlxd_softc *)MLX_BIO_SOFTC(bp); debug_called(1); /* bogus disk? */ if (sc == NULL) { MLX_BIO_SET_ERROR(bp, EINVAL); goto bad; } /* XXX may only be temporarily offline - sleep? */ if (sc->mlxd_drive->ms_state == MLX_SYSD_OFFLINE) { MLX_BIO_SET_ERROR(bp, ENXIO); goto bad; } MLX_BIO_STATS_START(bp); mlx_submit_buf(sc->mlxd_controller, bp); return; bad: /* * Correctly set the bio to indicate a failed tranfer. */ MLX_BIO_RESID(bp) = MLX_BIO_LENGTH(bp); MLX_BIO_DONE(bp); return; }
static void _reject(isc_session_t *sp, pduq_t *pq) { pduq_t *opq; pdu_t *pdu; reject_t *reject; int itt; debug_called(8); pdu = mtod(pq->mp, pdu_t *); itt = pdu->ipdu.bhs.itt; reject = &pq->pdu.ipdu.reject; sdebug(2, "itt=%x reason=0x%x", ntohl(itt), reject->reason); opq = i_search_hld(sp, itt, 0); if(opq != NULL) iscsi_reject(sp, opq, pq); else { switch(pq->pdu.ipdu.bhs.opcode) { case ISCSI_LOGOUT_CMD: // XXX: wasabi does this - can't figure out why sdebug(2, "ISCSI_LOGOUT_CMD ..."); break; default: xdebug("%d] we lost something itt=%x", sp->sid, ntohl(pq->pdu.ipdu.bhs.itt)); } } pdu_free(sp->isc, pq); }
static trans_t doLogin(isess_t *sess) { isc_opt_t *op = sess->op; int status, count; debug_called(3); if(op->chapSecret == NULL && op->tgtChapSecret == NULL) /* | don't need any security negotiation | or in other words: we don't have any secrets to exchange */ sess->csg = LON_PHASE; else sess->csg = SN_PHASE; if(sess->tsih) { sess->tsih = 0; // XXX: no 'reconnect' yet sess->flags &= ~SESS_NEGODONE; // XXX: KLUDGE } count = 10; // should be more than enough do { debug(3, "count=%d csg=%d", count, sess->csg); status = loginPhase(sess); if(count-- == 0) // just in case we get into a loop status = -1; } while(status == 0 && (sess->csg != FF_PHASE)); sess->flags &= ~SESS_INITIALLOGIN; debug(3, "status=%d", status); switch(status) { case 0: // all is ok ... sess->flags |= SESS_LOGGEDIN; if(strcmp(sess->op->sessionType, "Discovery") == 0) doDiscovery(sess); return T5; case 1: // redirect - temporary/permanent /* | start from scratch? */ sess->flags &= ~SESS_NEGODONE; sess->flags |= (SESS_REDIRECT | SESS_INITIALLOGIN1); syslog(LOG_DEBUG, "target sent REDIRECT"); return T7; case 2: // initiator terminal error return 0; case 3: // target terminal error -- could retry ... sleep(5); return T7; // lets try default: return 0; } }
int ic_init(isc_session_t *sp) { struct cam_sim *sim; struct cam_devq *devq; debug_called(8); if((devq = cam_simq_alloc(256)) == NULL) return ENOMEM; #if __FreeBSD_version >= 700000 mtx_init(&sp->cam_mtx, "isc-cam", NULL, MTX_DEF); #else isp->cam_mtx = Giant; #endif sim = cam_sim_alloc(ic_action, ic_poll, "iscsi", sp, sp->sid, // unit #if __FreeBSD_version >= 700000 &sp->cam_mtx, #endif 1, // max_dev_transactions 0, // max_tagged_dev_transactions devq); if(sim == NULL) { cam_simq_free(devq); #if __FreeBSD_version >= 700000 mtx_destroy(&sp->cam_mtx); #endif return ENXIO; } CAM_LOCK(sp); if(xpt_bus_register(sim, #if __FreeBSD_version >= 700000 NULL, #endif 0/*bus_number*/) != CAM_SUCCESS) { cam_sim_free(sim, /*free_devq*/TRUE); CAM_UNLOCK(sp); #if __FreeBSD_version >= 700000 mtx_destroy(&sp->cam_mtx); #endif return ENXIO; } sp->cam_sim = sim; CAM_UNLOCK(sp); sdebug(1, "cam subsystem initialized"); ic_scan(sp); return 0; }
/******************************************************************************** * Check for possibly-completed commands. */ static void mly_cam_poll(struct cam_sim *sim) { struct mly_softc *sc = cam_sim_softc(sim); debug_called(2); mly_done(sc); }
static int mlxd_probe(device_t dev) { debug_called(1); device_set_desc(dev, "Mylex System Drive"); return (0); }
static void _async(isc_session_t *sp, pduq_t *pq) { debug_called(8); iscsi_async(sp, pq); pdu_free(sp->isc, pq); }
/******************************************************************************** * Save the physical address of the memory mailbox */ static void mly_mmbox_map_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct mly_softc *sc = (struct mly_softc *)arg; debug_called(2); sc->mly_mmbox_busaddr = segs->ds_addr; }
static int handleLoginResp(isess_t *sess, pdu_t *pp) { login_rsp_t *lp = (login_rsp_t *)pp; uint st_class, status = ntohs(lp->status); debug_called(3); debug(4, "Tbit=%d csg=%d nsg=%d status=%x", lp->T, lp->CSG, lp->NSG, status); st_class = status >> 8; if(status) { int st_detail = status & 0xff; switch(st_class) { case 1: // Redirect switch(st_detail) { // the ITN (iSCSI target Name) requests a: case 1: // temporary address change case 2: // permanent address change status = 0; } break; case 2: // Initiator Error if(st_detail < CLASS1_ERRS) printf("0x%04x: %s\n", status, status_class1[st_detail]); break; case 3: if(st_detail < CLASS3_ERRS) printf("0x%04x: %s\n", status, status_class3[st_detail]); break; } } if(status == 0) { processParams(sess, pp); setOptions(sess, 0); // XXX: just in case ... if(lp->T) { isc_opt_t *op = sess->op; if(sess->csg == SN_PHASE && (op->tgtChapDigest != NULL)) if(handleTgtResp(sess, pp) != 0) return 1; // XXX: Authentication failure ... sess->csg = lp->NSG; if(sess->csg == FF_PHASE) { // XXX: will need this when implementing reconnect. sess->tsih = lp->tsih; debug(2, "TSIH=%x", sess->tsih); } } } return st_class; }
/******************************************************************************** * Free all of the resources associated with (sc) * * Should not be called if the controller is active. */ static void amr_pci_free(struct amr_softc *sc) { void *p; debug_called(1); amr_free(sc); /* destroy data-transfer DMA tag */ if (sc->amr_buffer_dmat) bus_dma_tag_destroy(sc->amr_buffer_dmat); if (sc->amr_buffer64_dmat) bus_dma_tag_destroy(sc->amr_buffer64_dmat); /* free and destroy DMA memory and tag for passthrough pool */ if (sc->amr_ccb) { bus_dmamap_unload(sc->amr_ccb_dmat, sc->amr_ccb_dmamap); bus_dmamem_free(sc->amr_ccb_dmat, sc->amr_ccb, sc->amr_ccb_dmamap); } if (sc->amr_ccb_dmat) bus_dma_tag_destroy(sc->amr_ccb_dmat); /* free and destroy DMA memory and tag for s/g lists */ if (sc->amr_sgtable) { bus_dmamap_unload(sc->amr_sg_dmat, sc->amr_sg_dmamap); bus_dmamem_free(sc->amr_sg_dmat, sc->amr_sgtable, sc->amr_sg_dmamap); } if (sc->amr_sg_dmat) bus_dma_tag_destroy(sc->amr_sg_dmat); /* free and destroy DMA memory and tag for mailbox */ p = (void *)(uintptr_t)(volatile void *)sc->amr_mailbox64; if (sc->amr_mailbox) { bus_dmamap_unload(sc->amr_mailbox_dmat, sc->amr_mailbox_dmamap); bus_dmamem_free(sc->amr_mailbox_dmat, p, sc->amr_mailbox_dmamap); } if (sc->amr_mailbox_dmat) bus_dma_tag_destroy(sc->amr_mailbox_dmat); /* disconnect the interrupt handler */ if (sc->amr_intr) bus_teardown_intr(sc->amr_dev, sc->amr_irq, sc->amr_intr); if (sc->amr_irq != NULL) bus_release_resource(sc->amr_dev, SYS_RES_IRQ, 0, sc->amr_irq); /* destroy the parent DMA tag */ if (sc->amr_parent_dmat) bus_dma_tag_destroy(sc->amr_parent_dmat); /* release the register window mapping */ if (sc->amr_reg != NULL) bus_release_resource(sc->amr_dev, AMR_IS_QUARTZ(sc) ? SYS_RES_MEMORY : SYS_RES_IOPORT, PCIR_BAR(0), sc->amr_reg); }
/******************************************************************************** * Free all of the resources associated with (sc) * * Should not be called if the controller is active. */ void mly_free(struct mly_softc *sc) { struct mly_command_cluster *mcc; debug_called(1); /* detach from CAM */ mly_cam_detach(sc); /* throw away any command buffers */ while ((mcc = mly_dequeue_cluster(sc)) != NULL) mly_free_command_cluster(mcc); /* throw away the controllerinfo structure */ if (sc->mly_controllerinfo != NULL) free(sc->mly_controllerinfo, M_DEVBUF); /* throw away the controllerparam structure */ if (sc->mly_controllerparam != NULL) free(sc->mly_controllerparam, M_DEVBUF); /* destroy data-transfer DMA tag */ if (sc->mly_buffer_dmat) bus_dma_tag_destroy(sc->mly_buffer_dmat); /* free and destroy DMA memory and tag for s/g lists */ if (sc->mly_sg_table) { bus_dmamap_unload(sc->mly_sg_dmat, sc->mly_sg_dmamap); bus_dmamem_free(sc->mly_sg_dmat, sc->mly_sg_table, sc->mly_sg_dmamap); } if (sc->mly_sg_dmat) bus_dma_tag_destroy(sc->mly_sg_dmat); /* free and destroy DMA memory and tag for memory mailbox */ if (sc->mly_mmbox) { bus_dmamap_unload(sc->mly_mmbox_dmat, sc->mly_mmbox_dmamap); bus_dmamem_free(sc->mly_mmbox_dmat, sc->mly_mmbox, sc->mly_mmbox_dmamap); } if (sc->mly_mmbox_dmat) bus_dma_tag_destroy(sc->mly_mmbox_dmat); /* disconnect the interrupt handler */ if (sc->mly_intr) bus_teardown_intr(sc->mly_dev, sc->mly_irq, sc->mly_intr); if (sc->mly_irq != NULL) bus_release_resource(sc->mly_dev, SYS_RES_IRQ, sc->mly_irq_rid, sc->mly_irq); /* destroy the parent DMA tag */ if (sc->mly_parent_dmat) bus_dma_tag_destroy(sc->mly_parent_dmat); /* release the register window mapping */ if (sc->mly_regs_resource != NULL) bus_release_resource(sc->mly_dev, SYS_RES_MEMORY, sc->mly_regs_rid, sc->mly_regs_resource); }
/******************************************************************************** * Save the physical address of the base of the s/g table. */ static void mly_sg_map_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct mly_softc *sc = (struct mly_softc *)arg; debug_called(2); /* save base of s/g table's address in bus space */ sc->mly_sg_busaddr = segs->ds_addr; }
/******************************************************************************* * Take an interrupt, or be poked by other code to look for interrupt-worthy * status. */ static void amr_pci_intr(void *arg) { struct amr_softc *sc = (struct amr_softc *)arg; debug_called(3); /* collect finished commands, queue anything waiting */ amr_done(sc); }
/******************************************************************************** * Bring the controller down to a dormant state and detach all child devices. * * This function is called before detach or system shutdown. * * Note that we can assume that the camq on the controller is empty, as we won't * allow shutdown if any device is open. */ static int mly_pci_shutdown(device_t dev) { struct mly_softc *sc = device_get_softc(dev); debug_called(1); mly_detach(sc); return(0); }
/******************************************************************************** * Allocate and map the scatter/gather table in bus space. */ static void amr_sglist_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error) { uint32_t *addr; debug_called(1); addr = arg; *addr = segs[0].ds_addr; }
/******************************************************************************** * Bring the controller back to a state ready for operation. */ static int mly_pci_resume(device_t dev) { struct mly_softc *sc = device_get_softc(dev); debug_called(1); sc->mly_state &= ~MLY_STATE_SUSPEND; MLY_UNMASK_INTERRUPTS(sc); return(0); }